Project import generated by Copybara.

GitOrigin-RevId: 1849e9ac6ad0564fcefbdaf7989e692fe8e7226d
diff --git a/drivers/ddr/marvell/a38x/Makefile b/drivers/ddr/marvell/a38x/Makefile
new file mode 100644
index 0000000..8251d6d
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_SPL_BUILD) += mv_ddr_plat.o
+obj-$(CONFIG_SPL_BUILD) += mv_ddr_sys_env_lib.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_debug.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_init.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_bist.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_centralization.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_db.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_hw_algo.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_ip_engine.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_leveling.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_training_pbs.o
+obj-$(CONFIG_SPL_BUILD) += mv_ddr_build_message.o
+obj-$(CONFIG_SPL_BUILD) += mv_ddr_common.o
+obj-$(CONFIG_SPL_BUILD) += mv_ddr_spd.o
+obj-$(CONFIG_SPL_BUILD) += mv_ddr_topology.o
+obj-$(CONFIG_SPL_BUILD) += xor.o
diff --git a/drivers/ddr/marvell/a38x/ddr3_debug.c b/drivers/ddr/marvell/a38x/ddr3_debug.c
new file mode 100644
index 0000000..f5fc964
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_debug.c
@@ -0,0 +1,1386 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_training_db.h"
+#include "mv_ddr_regs.h"
+
+u8 is_reg_dump = 0;
+u8 debug_pbs = DEBUG_LEVEL_ERROR;
+
+/*
+ * API to change flags outside of the lib
+ */
+#if defined(SILENT_LIB)
+void ddr3_hws_set_log_level(enum ddr_lib_debug_block block, u8 level)
+{
+	/* do nothing */
+}
+#else /* SILENT_LIB */
+/* Debug flags for other Training modules */
+u8 debug_training_static = DEBUG_LEVEL_ERROR;
+u8 debug_training = DEBUG_LEVEL_ERROR;
+u8 debug_leveling = DEBUG_LEVEL_ERROR;
+u8 debug_centralization = DEBUG_LEVEL_ERROR;
+u8 debug_training_ip = DEBUG_LEVEL_ERROR;
+u8 debug_training_bist = DEBUG_LEVEL_ERROR;
+u8 debug_training_hw_alg = DEBUG_LEVEL_ERROR;
+u8 debug_training_access = DEBUG_LEVEL_ERROR;
+u8 debug_training_device = DEBUG_LEVEL_ERROR;
+
+
+void mv_ddr_user_log_level_set(enum ddr_lib_debug_block block)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	ddr3_hws_set_log_level(block, tm->debug_level);
+};
+
+void ddr3_hws_set_log_level(enum ddr_lib_debug_block block, u8 level)
+{
+	switch (block) {
+	case DEBUG_BLOCK_STATIC:
+		debug_training_static = level;
+		break;
+	case DEBUG_BLOCK_TRAINING_MAIN:
+		debug_training = level;
+		break;
+	case DEBUG_BLOCK_LEVELING:
+		debug_leveling = level;
+		break;
+	case DEBUG_BLOCK_CENTRALIZATION:
+		debug_centralization = level;
+		break;
+	case DEBUG_BLOCK_PBS:
+		debug_pbs = level;
+		break;
+	case DEBUG_BLOCK_ALG:
+		debug_training_hw_alg = level;
+		break;
+	case DEBUG_BLOCK_DEVICE:
+		debug_training_device = level;
+		break;
+	case DEBUG_BLOCK_ACCESS:
+		debug_training_access = level;
+		break;
+	case DEBUG_STAGES_REG_DUMP:
+		if (level == DEBUG_LEVEL_TRACE)
+			is_reg_dump = 1;
+		else
+			is_reg_dump = 0;
+		break;
+	case DEBUG_BLOCK_ALL:
+	default:
+		debug_training_static = level;
+		debug_training = level;
+		debug_leveling = level;
+		debug_centralization = level;
+		debug_pbs = level;
+		debug_training_hw_alg = level;
+		debug_training_access = level;
+		debug_training_device = level;
+	}
+}
+#endif /* SILENT_LIB */
+
+#if defined(DDR_VIEWER_TOOL)
+static char *convert_freq(enum mv_ddr_freq freq);
+#if defined(EXCLUDE_SWITCH_DEBUG)
+u32 ctrl_sweepres[ADLL_LENGTH][MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u32 ctrl_adll[MAX_CS_NUM * MAX_INTERFACE_NUM * MAX_BUS_NUM];
+u32 ctrl_adll1[MAX_CS_NUM * MAX_INTERFACE_NUM * MAX_BUS_NUM];
+u32 ctrl_level_phase[MAX_CS_NUM * MAX_INTERFACE_NUM * MAX_BUS_NUM];
+#endif /* EXCLUDE_SWITCH_DEBUG */
+#endif /* DDR_VIEWER_TOOL */
+
+struct hws_tip_config_func_db config_func_info[MAX_DEVICE_NUM];
+u8 is_default_centralization = 0;
+u8 is_tune_result = 0;
+u8 is_validate_window_per_if = 0;
+u8 is_validate_window_per_pup = 0;
+u8 sweep_cnt = 1;
+u32 is_bist_reset_bit = 1;
+u8 is_run_leveling_sweep_tests;
+
+static struct hws_xsb_info xsb_info[MAX_DEVICE_NUM];
+
+/*
+ * Dump Dunit & Phy registers
+ */
+int ddr3_tip_reg_dump(u32 dev_num)
+{
+	u32 if_id, reg_addr, data_value, bus_id;
+	u32 read_data[MAX_INTERFACE_NUM];
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	printf("-- dunit registers --\n");
+	for (reg_addr = 0x1400; reg_addr < 0x19f0; reg_addr += 4) {
+		printf("0x%x ", reg_addr);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			CHECK_STATUS(ddr3_tip_if_read
+				     (dev_num, ACCESS_TYPE_UNICAST,
+				      if_id, reg_addr, read_data,
+				      MASK_ALL_BITS));
+			printf("0x%x ", read_data[if_id]);
+		}
+		printf("\n");
+	}
+
+	printf("-- Phy registers --\n");
+	for (reg_addr = 0; reg_addr <= 0xff; reg_addr++) {
+		printf("0x%x ", reg_addr);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			for (bus_id = 0;
+			     bus_id < octets_per_if_num;
+			     bus_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, bus_id,
+					      DDR_PHY_DATA, reg_addr,
+					      &data_value));
+				printf("0x%x ", data_value);
+			}
+			for (bus_id = 0;
+			     bus_id < octets_per_if_num;
+			     bus_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, bus_id,
+					      DDR_PHY_CONTROL, reg_addr,
+					      &data_value));
+				printf("0x%x ", data_value);
+			}
+		}
+		printf("\n");
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Register access func registration
+ */
+int ddr3_tip_init_config_func(u32 dev_num,
+			      struct hws_tip_config_func_db *config_func)
+{
+	if (config_func == NULL)
+		return MV_BAD_PARAM;
+
+	memcpy(&config_func_info[dev_num], config_func,
+	       sizeof(struct hws_tip_config_func_db));
+
+	return MV_OK;
+}
+
+/*
+ * Get training result info pointer
+ */
+enum hws_result *ddr3_tip_get_result_ptr(u32 stage)
+{
+	return training_result[stage];
+}
+
+/*
+ * Device info read
+ */
+int ddr3_tip_get_device_info(u32 dev_num, struct ddr3_device_info *info_ptr)
+{
+	if (config_func_info[dev_num].tip_get_device_info_func != NULL) {
+		return config_func_info[dev_num].
+			tip_get_device_info_func((u8) dev_num, info_ptr);
+	}
+
+	return MV_FAIL;
+}
+
+#if defined(DDR_VIEWER_TOOL)
+/*
+ * Convert freq to character string
+ */
+static char *convert_freq(enum mv_ddr_freq freq)
+{
+	switch (freq) {
+	case MV_DDR_FREQ_LOW_FREQ:
+		return "MV_DDR_FREQ_LOW_FREQ";
+
+	case MV_DDR_FREQ_400:
+		return "400";
+
+	case MV_DDR_FREQ_533:
+		return "533";
+
+	case MV_DDR_FREQ_667:
+		return "667";
+
+	case MV_DDR_FREQ_800:
+		return "800";
+
+	case MV_DDR_FREQ_933:
+		return "933";
+
+	case MV_DDR_FREQ_1066:
+		return "1066";
+
+	case MV_DDR_FREQ_311:
+		return "311";
+
+	case MV_DDR_FREQ_333:
+		return "333";
+
+	case MV_DDR_FREQ_467:
+		return "467";
+
+	case MV_DDR_FREQ_850:
+		return "850";
+
+	case MV_DDR_FREQ_900:
+		return "900";
+
+	case MV_DDR_FREQ_360:
+		return "MV_DDR_FREQ_360";
+
+	case MV_DDR_FREQ_1000:
+		return "MV_DDR_FREQ_1000";
+
+	default:
+		return "Unknown Frequency";
+	}
+}
+
+/*
+ * Convert device ID to character string
+ */
+static char *convert_dev_id(u32 dev_id)
+{
+	switch (dev_id) {
+	case 0x6800:
+		return "A38xx";
+	case 0x6900:
+		return "A39XX";
+	case 0xf400:
+		return "AC3";
+	case 0xfc00:
+		return "BC2";
+
+	default:
+		return "Unknown Device";
+	}
+}
+
+/*
+ * Convert device ID to character string
+ */
+static char *convert_mem_size(u32 dev_id)
+{
+	switch (dev_id) {
+	case 0:
+		return "512 MB";
+	case 1:
+		return "1 GB";
+	case 2:
+		return "2 GB";
+	case 3:
+		return "4 GB";
+	case 4:
+		return "8 GB";
+
+	default:
+		return "wrong mem size";
+	}
+}
+
+int print_device_info(u8 dev_num)
+{
+	struct ddr3_device_info info_ptr;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	CHECK_STATUS(ddr3_tip_get_device_info(dev_num, &info_ptr));
+	printf("=== DDR setup START===\n");
+	printf("\tDevice ID: %s\n", convert_dev_id(info_ptr.device_id));
+	printf("\tDDR3  CK delay: %d\n", info_ptr.ck_delay);
+	print_topology(tm);
+	printf("=== DDR setup END===\n");
+
+	return MV_OK;
+}
+
+void hws_ddr3_tip_sweep_test(int enable)
+{
+	if (enable) {
+		is_validate_window_per_if = 1;
+		is_validate_window_per_pup = 1;
+		debug_training = DEBUG_LEVEL_TRACE;
+	} else {
+		is_validate_window_per_if = 0;
+		is_validate_window_per_pup = 0;
+	}
+}
+#endif /* DDR_VIEWER_TOOL */
+
+char *ddr3_tip_convert_tune_result(enum hws_result tune_result)
+{
+	switch (tune_result) {
+	case TEST_FAILED:
+		return "FAILED";
+	case TEST_SUCCESS:
+		return "PASS";
+	case NO_TEST_DONE:
+		return "NOT COMPLETED";
+	default:
+		return "Un-KNOWN";
+	}
+}
+
+/*
+ * Print log info
+ */
+int ddr3_tip_print_log(u32 dev_num, u32 mem_addr)
+{
+	u32 if_id = 0;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+#if defined(DDR_VIEWER_TOOL)
+	if ((is_validate_window_per_if != 0) ||
+	    (is_validate_window_per_pup != 0)) {
+		u32 is_pup_log = 0;
+		enum mv_ddr_freq freq;
+
+		freq = tm->interface_params[first_active_if].memory_freq;
+
+		is_pup_log = (is_validate_window_per_pup != 0) ? 1 : 0;
+		printf("===VALIDATE WINDOW LOG START===\n");
+		printf("DDR Frequency: %s   ======\n", convert_freq(freq));
+		/* print sweep windows */
+		ddr3_tip_run_sweep_test(dev_num, sweep_cnt, 1, is_pup_log);
+		ddr3_tip_run_sweep_test(dev_num, sweep_cnt, 0, is_pup_log);
+#if defined(EXCLUDE_SWITCH_DEBUG)
+		if (is_run_leveling_sweep_tests == 1) {
+			ddr3_tip_run_leveling_sweep_test(dev_num, sweep_cnt, 0, is_pup_log);
+			ddr3_tip_run_leveling_sweep_test(dev_num, sweep_cnt, 1, is_pup_log);
+		}
+#endif /* EXCLUDE_SWITCH_DEBUG */
+		ddr3_tip_print_all_pbs_result(dev_num);
+		ddr3_tip_print_wl_supp_result(dev_num);
+		printf("===VALIDATE WINDOW LOG END ===\n");
+		CHECK_STATUS(ddr3_tip_restore_dunit_regs(dev_num));
+		ddr3_tip_reg_dump(dev_num);
+	}
+#endif /* DDR_VIEWER_TOOL */
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("IF %d Status:\n", if_id));
+
+		if (mask_tune_func & INIT_CONTROLLER_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tInit Controller: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[INIT_CONTROLLER]
+					    [if_id])));
+		}
+		if (mask_tune_func & SET_LOW_FREQ_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tLow freq Config: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[SET_LOW_FREQ]
+					    [if_id])));
+		}
+		if (mask_tune_func & LOAD_PATTERN_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tLoad Pattern: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[LOAD_PATTERN]
+					    [if_id])));
+		}
+		if (mask_tune_func & SET_MEDIUM_FREQ_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tMedium freq Config: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[SET_MEDIUM_FREQ]
+					    [if_id])));
+		}
+		if (mask_tune_func & WRITE_LEVELING_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tWL: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[WRITE_LEVELING]
+					    [if_id])));
+		}
+		if (mask_tune_func & LOAD_PATTERN_2_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tLoad Pattern: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[LOAD_PATTERN_2]
+					    [if_id])));
+		}
+		if (mask_tune_func & READ_LEVELING_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tRL: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[READ_LEVELING]
+					    [if_id])));
+		}
+		if (mask_tune_func & WRITE_LEVELING_SUPP_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tWL Supp: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[WRITE_LEVELING_SUPP]
+					    [if_id])));
+		}
+		if (mask_tune_func & PBS_RX_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tPBS RX: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[PBS_RX]
+					    [if_id])));
+		}
+		if (mask_tune_func & PBS_TX_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tPBS TX: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[PBS_TX]
+					    [if_id])));
+		}
+		if (mask_tune_func & SET_TARGET_FREQ_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tTarget freq Config: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[SET_TARGET_FREQ]
+					    [if_id])));
+		}
+		if (mask_tune_func & WRITE_LEVELING_TF_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tWL TF: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[WRITE_LEVELING_TF]
+					    [if_id])));
+		}
+		if (mask_tune_func & READ_LEVELING_TF_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tRL TF: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[READ_LEVELING_TF]
+					    [if_id])));
+		}
+		if (mask_tune_func & WRITE_LEVELING_SUPP_TF_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tWL TF Supp: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result
+					    [WRITE_LEVELING_SUPP_TF]
+					    [if_id])));
+		}
+		if (mask_tune_func & CENTRALIZATION_RX_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tCentr RX: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[CENTRALIZATION_RX]
+					    [if_id])));
+		}
+		if (mask_tune_func & VREF_CALIBRATION_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tVREF_CALIBRATION: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[VREF_CALIBRATION]
+					    [if_id])));
+		}
+		if (mask_tune_func & CENTRALIZATION_TX_MASK_BIT) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("\tCentr TX: %s\n",
+					   ddr3_tip_convert_tune_result
+					   (training_result[CENTRALIZATION_TX]
+					    [if_id])));
+		}
+	}
+
+	return MV_OK;
+}
+
+#if !defined(EXCLUDE_DEBUG_PRINTS)
+/*
+ * Print stability log info
+ */
+int ddr3_tip_print_stability_log(u32 dev_num)
+{
+	u8 if_id = 0, csindex = 0, bus_id = 0, idx = 0;
+	u32 reg_data;
+	u32 read_data[MAX_INTERFACE_NUM];
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* Title print */
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		printf("Title: I/F# , Tj, Calibration_n0, Calibration_p0, Calibration_n1, Calibration_p1, Calibration_n2, Calibration_p2,");
+		for (csindex = 0; csindex < max_cs; csindex++) {
+			printf("CS%d , ", csindex);
+			printf("\n");
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			printf("VWTx, VWRx, WL_tot, WL_ADLL, WL_PH, RL_Tot, RL_ADLL, RL_PH, RL_Smp, Cen_tx, Cen_rx, Vref, DQVref,");
+			printf("\t\t");
+			for (idx = 0; idx < 11; idx++)
+				printf("PBSTx-Pad%d,", idx);
+			printf("\t\t");
+			for (idx = 0; idx < 11; idx++)
+				printf("PBSRx-Pad%d,", idx);
+		}
+	}
+	printf("\n");
+
+	/* Data print */
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		printf("Data: %d,%d,", if_id,
+		       (config_func_info[dev_num].tip_get_temperature != NULL)
+		       ? (config_func_info[dev_num].
+			  tip_get_temperature(dev_num)) : (0));
+
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x14c8,
+			      read_data, MASK_ALL_BITS));
+		printf("%d,%d,", ((read_data[if_id] & 0x3f0) >> 4),
+		       ((read_data[if_id] & 0xfc00) >> 10));
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x17c8,
+			      read_data, MASK_ALL_BITS));
+		printf("%d,%d,", ((read_data[if_id] & 0x3f0) >> 4),
+		       ((read_data[if_id] & 0xfc00) >> 10));
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x1dc8,
+			      read_data, MASK_ALL_BITS));
+		printf("%d,%d,", ((read_data[if_id] & 0x3f0000) >> 16),
+		       ((read_data[if_id] & 0xfc00000) >> 22));
+
+		for (csindex = 0; csindex < max_cs; csindex++) {
+			printf("CS%d , ", csindex);
+			for (bus_id = 0; bus_id < MAX_BUS_NUM; bus_id++) {
+				printf("\n");
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST,
+						  bus_id, DDR_PHY_DATA,
+						  RESULT_PHY_REG +
+						  csindex, &reg_data);
+				printf("%d,%d,", (reg_data & 0x1f),
+				       ((reg_data & 0x3e0) >> 5));
+				/* WL */
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST,
+						  bus_id, DDR_PHY_DATA,
+						  WL_PHY_REG(csindex),
+						  &reg_data);
+				printf("%d,%d,%d,",
+				       (reg_data & 0x1f) +
+				       ((reg_data & 0x1c0) >> 6) * 32,
+				       (reg_data & 0x1f),
+				       (reg_data & 0x1c0) >> 6);
+				/* RL */
+				CHECK_STATUS(ddr3_tip_if_read
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id,
+					      RD_DATA_SMPL_DLYS_REG,
+					      read_data, MASK_ALL_BITS));
+				read_data[if_id] =
+					(read_data[if_id] &
+					 (0x1f << (8 * csindex))) >>
+					(8 * csindex);
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_id,
+						  DDR_PHY_DATA,
+						  RL_PHY_REG(csindex),
+						  &reg_data);
+				printf("%d,%d,%d,%d,",
+				       (reg_data & 0x1f) +
+				       ((reg_data & 0x1c0) >> 6) * 32 +
+				       read_data[if_id] * 64,
+				       (reg_data & 0x1f),
+				       ((reg_data & 0x1c0) >> 6),
+				       read_data[if_id]);
+				/* Centralization */
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_id,
+						  DDR_PHY_DATA,
+						  CTX_PHY_REG(csindex),
+						  &reg_data);
+				printf("%d,", (reg_data & 0x3f));
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_id,
+						  DDR_PHY_DATA,
+						  CRX_PHY_REG(csindex),
+						   &reg_data);
+				printf("%d,", (reg_data & 0x1f));
+				/* Vref */
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_id,
+						  DDR_PHY_DATA,
+						  PAD_CFG_PHY_REG,
+						  &reg_data);
+				printf("%d,", (reg_data & 0x7));
+				/* DQVref */
+				/* Need to add the Read Function from device */
+				printf("%d,", 0);
+				printf("\t\t");
+				for (idx = 0; idx < 11; idx++) {
+					ddr3_tip_bus_read(dev_num, if_id,
+							  ACCESS_TYPE_UNICAST,
+							  bus_id, DDR_PHY_DATA,
+							  0x10 +
+							  16 * csindex +
+							  idx, &reg_data);
+					printf("%d,", (reg_data & 0x3f));
+				}
+				printf("\t\t");
+				for (idx = 0; idx < 11; idx++) {
+					ddr3_tip_bus_read(dev_num, if_id,
+							  ACCESS_TYPE_UNICAST,
+							  bus_id, DDR_PHY_DATA,
+							  0x50 +
+							  16 * csindex +
+							  idx, &reg_data);
+					printf("%d,", (reg_data & 0x3f));
+				}
+			}
+		}
+	}
+	printf("\n");
+
+	return MV_OK;
+}
+#endif /* EXCLUDE_DEBUG_PRINTS */
+
+/*
+ * Register XSB information
+ */
+int ddr3_tip_register_xsb_info(u32 dev_num, struct hws_xsb_info *xsb_info_table)
+{
+	memcpy(&xsb_info[dev_num], xsb_info_table, sizeof(struct hws_xsb_info));
+	return MV_OK;
+}
+
+/*
+ * Read ADLL Value
+ */
+int ddr3_tip_read_adll_value(u32 dev_num, u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM],
+			     u32 reg_addr, u32 mask)
+{
+	u32 data_value;
+	u32 if_id = 0, bus_id = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/*
+	 * multi CS support - reg_addr is calucalated in calling function
+	 * with CS offset
+	 */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0; bus_id < octets_per_if_num;
+		     bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			CHECK_STATUS(ddr3_tip_bus_read(dev_num, if_id,
+						       ACCESS_TYPE_UNICAST,
+						       bus_id,
+						       DDR_PHY_DATA, reg_addr,
+						       &data_value));
+			pup_values[if_id *
+				   octets_per_if_num + bus_id] =
+				data_value & mask;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Write ADLL Value
+ */
+int ddr3_tip_write_adll_value(u32 dev_num, u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM],
+			      u32 reg_addr)
+{
+	u32 if_id = 0, bus_id = 0;
+	u32 data;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/*
+	 * multi CS support - reg_addr is calucalated in calling function
+	 * with CS offset
+	 */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0; bus_id < octets_per_if_num;
+		     bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			data = pup_values[if_id *
+					  octets_per_if_num +
+					  bus_id];
+			CHECK_STATUS(ddr3_tip_bus_write(dev_num,
+							ACCESS_TYPE_UNICAST,
+							if_id,
+							ACCESS_TYPE_UNICAST,
+							bus_id, DDR_PHY_DATA,
+							reg_addr, data));
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Read Phase Value
+ */
+int read_phase_value(u32 dev_num, u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM],
+		     int reg_addr, u32 mask)
+{
+	u32  data_value;
+	u32 if_id = 0, bus_id = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* multi CS support - reg_addr is calucalated in calling function with CS offset */
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			CHECK_STATUS(ddr3_tip_bus_read(dev_num, if_id,
+						       ACCESS_TYPE_UNICAST,
+						       bus_id,
+						       DDR_PHY_DATA, reg_addr,
+						       &data_value));
+			pup_values[if_id * octets_per_if_num + bus_id] = data_value & mask;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Write Leveling Value
+ */
+int write_leveling_value(u32 dev_num, u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM],
+			 u32 pup_ph_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], int reg_addr)
+{
+	u32 if_id = 0, bus_id = 0;
+	u32 data;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* multi CS support - reg_addr is calucalated in calling function with CS offset */
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0 ; bus_id < octets_per_if_num ; bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			data = pup_values[if_id * octets_per_if_num + bus_id] +
+			       pup_ph_values[if_id * octets_per_if_num + bus_id];
+			CHECK_STATUS(ddr3_tip_bus_write(dev_num,
+							ACCESS_TYPE_UNICAST,
+							if_id,
+							ACCESS_TYPE_UNICAST,
+							bus_id,
+							DDR_PHY_DATA,
+							reg_addr,
+							data));
+		}
+	}
+
+	return 0;
+}
+
+#if !defined(EXCLUDE_SWITCH_DEBUG)
+struct hws_tip_config_func_db config_func_info[MAX_DEVICE_NUM];
+u32 start_xsb_offset = 0;
+u8 is_rl_old = 0;
+u8 is_freq_old = 0;
+u8 is_dfs_disabled = 0;
+u32 default_centrlization_value = 0x12;
+u32 activate_select_before_run_alg = 1, activate_deselect_after_run_alg = 1,
+	rl_test = 0, reset_read_fifo = 0;
+int debug_acc = 0;
+u32 ctrl_sweepres[ADLL_LENGTH][MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u32 ctrl_adll[MAX_CS_NUM * MAX_INTERFACE_NUM * MAX_BUS_NUM];
+
+u32 xsb_test_table[][8] = {
+	{0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555,
+	 0x66666666, 0x77777777},
+	{0x88888888, 0x99999999, 0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd,
+	 0xeeeeeeee, 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, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+	 0xffffffff, 0xffffffff},
+	{0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000,
+	 0x00000000, 0x00000000},
+	{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+	 0xffffffff, 0xffffffff}
+};
+
+int ddr3_tip_print_adll(void)
+{
+	u32 bus_cnt = 0, if_id, data_p1, data_p2, ui_data3, dev_num = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_cnt = 0; bus_cnt < octets_per_if_num;
+		     bus_cnt++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id,
+				      ACCESS_TYPE_UNICAST, bus_cnt,
+				      DDR_PHY_DATA, 0x1, &data_p1));
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id, ACCESS_TYPE_UNICAST,
+				      bus_cnt, DDR_PHY_DATA, 0x2, &data_p2));
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id, ACCESS_TYPE_UNICAST,
+				      bus_cnt, DDR_PHY_DATA, 0x3, &ui_data3));
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+					  (" IF %d bus_cnt %d  phy_reg_1_data 0x%x phy_reg_2_data 0x%x phy_reg_3_data 0x%x\n",
+					   if_id, bus_cnt, data_p1, data_p2,
+					   ui_data3));
+			}
+	}
+
+	return MV_OK;
+}
+
+#endif /* EXCLUDE_SWITCH_DEBUG */
+
+#if defined(DDR_VIEWER_TOOL)
+/*
+ * Print ADLL
+ */
+int print_adll(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM])
+{
+	u32 i, j;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (j = 0; j < octets_per_if_num; j++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, j);
+		for (i = 0; i < MAX_INTERFACE_NUM; i++)
+			printf("%d ,", adll[i * octets_per_if_num + j]);
+	}
+	printf("\n");
+
+	return MV_OK;
+}
+
+int print_ph(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM])
+{
+	u32 i, j;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (j = 0; j < octets_per_if_num; j++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, j);
+		for (i = 0; i < MAX_INTERFACE_NUM; i++)
+			printf("%d ,", adll[i * octets_per_if_num + j] >> 6);
+	}
+	printf("\n");
+
+	return MV_OK;
+}
+#endif /* DDR_VIEWER_TOOL */
+
+#if !defined(EXCLUDE_SWITCH_DEBUG)
+/* byte_index - only byte 0, 1, 2, or 3, oxff - test all bytes */
+static u32 ddr3_tip_compare(u32 if_id, u32 *p_src, u32 *p_dst,
+			    u32 byte_index)
+{
+	u32 burst_cnt = 0, addr_offset, i_id;
+	int b_is_fail = 0;
+
+	addr_offset =
+		(byte_index ==
+		 0xff) ? (u32) 0xffffffff : (u32) (0xff << (byte_index * 8));
+	for (burst_cnt = 0; burst_cnt < EXT_ACCESS_BURST_LENGTH; burst_cnt++) {
+		if ((p_src[burst_cnt] & addr_offset) !=
+		    (p_dst[if_id] & addr_offset))
+			b_is_fail = 1;
+	}
+
+	if (b_is_fail == 1) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("IF %d exp: ", if_id));
+		for (i_id = 0; i_id <= MAX_INTERFACE_NUM - 1; i_id++) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("0x%8x ", p_src[i_id]));
+		}
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("\n_i_f %d rcv: ", if_id));
+		for (i_id = 0; i_id <= MAX_INTERFACE_NUM - 1; i_id++) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("(0x%8x ", p_dst[i_id]));
+		}
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, ("\n "));
+	}
+
+	return b_is_fail;
+}
+#endif /* EXCLUDE_SWITCH_DEBUG */
+
+#if defined(DDR_VIEWER_TOOL)
+/*
+ * Sweep validation
+ */
+int ddr3_tip_run_sweep_test(int dev_num, u32 repeat_num, u32 direction,
+			    u32 mode)
+{
+	u32 pup = 0, start_pup = 0, end_pup = 0;
+	u32 adll = 0, rep = 0, pattern_idx = 0;
+	u32 res[MAX_INTERFACE_NUM] = { 0 };
+	int if_id = 0;
+	u32 adll_value = 0;
+	u32 reg;
+	enum hws_access_type pup_access;
+	u32 cs;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	repeat_num = 2;
+
+	if (mode == 1) {
+		/* per pup */
+		start_pup = 0;
+		end_pup = octets_per_if_num - 1;
+		pup_access = ACCESS_TYPE_UNICAST;
+	} else {
+		start_pup = 0;
+		end_pup = 0;
+		pup_access = ACCESS_TYPE_MULTICAST;
+	}
+
+	for (cs = 0; cs < max_cs; cs++) {
+		reg = (direction == 0) ? CTX_PHY_REG(cs) : CRX_PHY_REG(cs);
+		for (adll = 0; adll < ADLL_LENGTH; adll++) {
+			for (if_id = 0;
+			     if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE
+					(tm->if_act_mask,
+					 if_id);
+				for (pup = start_pup; pup <= end_pup; pup++) {
+					ctrl_sweepres[adll][if_id][pup] =
+						0;
+				}
+			}
+		}
+
+		for (adll = 0; adll < (MAX_INTERFACE_NUM * MAX_BUS_NUM); adll++)
+			ctrl_adll[adll] = 0;
+			/* Save DQS value(after algorithm run) */
+			ddr3_tip_read_adll_value(dev_num, ctrl_adll,
+						 reg, MASK_ALL_BITS);
+
+		/*
+		 * Sweep ADLL  from 0:31 on all I/F on all Pup and perform
+		 * BIST on each stage.
+		 */
+		for (pup = start_pup; pup <= end_pup; pup++) {
+			for (adll = 0; adll < ADLL_LENGTH; adll++) {
+				for (rep = 0; rep < repeat_num; rep++) {
+					for (pattern_idx = PATTERN_KILLER_DQ0;
+					     pattern_idx < PATTERN_LAST;
+					     pattern_idx++) {
+						adll_value =
+							(direction == 0) ? (adll * 2) : adll;
+						CHECK_STATUS(ddr3_tip_bus_write
+							     (dev_num, ACCESS_TYPE_MULTICAST, 0,
+							      pup_access, pup, DDR_PHY_DATA,
+							      reg, adll_value));
+						hws_ddr3_run_bist(dev_num, sweep_pattern, res,
+								  cs);
+						/* ddr3_tip_reset_fifo_ptr(dev_num); */
+						for (if_id = 0;
+						     if_id < MAX_INTERFACE_NUM;
+						     if_id++) {
+							VALIDATE_IF_ACTIVE
+								(tm->if_act_mask,
+								 if_id);
+							ctrl_sweepres[adll][if_id][pup]
+								+= res[if_id];
+							if (mode == 1) {
+								CHECK_STATUS
+									(ddr3_tip_bus_write
+									 (dev_num,
+									  ACCESS_TYPE_UNICAST,
+									  if_id,
+									  ACCESS_TYPE_UNICAST,
+									  pup,
+									  DDR_PHY_DATA,
+									  reg,
+									  ctrl_adll[if_id *
+										    cs *
+										    octets_per_if_num
+										    + pup]));
+							}
+						}
+					}
+				}
+			}
+		}
+		printf("Final, CS %d,%s, Sweep, Result, Adll,", cs,
+		       ((direction == 0) ? "TX" : "RX"));
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			if (mode == 1) {
+				for (pup = start_pup; pup <= end_pup; pup++) {
+					VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+					printf("I/F%d-PHY%d , ", if_id, pup);
+				}
+			} else {
+				printf("I/F%d , ", if_id);
+			}
+		}
+		printf("\n");
+
+		for (adll = 0; adll < ADLL_LENGTH; adll++) {
+			adll_value = (direction == 0) ? (adll * 2) : adll;
+			printf("Final,%s, Sweep, Result, %d ,",
+			       ((direction == 0) ? "TX" : "RX"), adll_value);
+
+			for (if_id = 0;
+			     if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				for (pup = start_pup; pup <= end_pup; pup++) {
+					printf("%8d , ",
+					       ctrl_sweepres[adll][if_id]
+					       [pup]);
+				}
+			}
+			printf("\n");
+		}
+
+		/*
+		 * Write back to the phy the Rx DQS value, we store in
+		 * the beginning.
+		 */
+		ddr3_tip_write_adll_value(dev_num, ctrl_adll, reg);
+		/* print adll results */
+		ddr3_tip_read_adll_value(dev_num, ctrl_adll, reg, MASK_ALL_BITS);
+		printf("%s, DQS, ADLL,,,", (direction == 0) ? "Tx" : "Rx");
+		print_adll(dev_num, ctrl_adll);
+	}
+	ddr3_tip_reset_fifo_ptr(dev_num);
+
+	return 0;
+}
+
+#if defined(EXCLUDE_SWITCH_DEBUG)
+int ddr3_tip_run_leveling_sweep_test(int dev_num, u32 repeat_num,
+				     u32 direction, u32 mode)
+{
+	u32 pup = 0, start_pup = 0, end_pup = 0, start_adll = 0;
+	u32 adll = 0, rep = 0, pattern_idx = 0;
+	u32 read_data[MAX_INTERFACE_NUM];
+	u32 res[MAX_INTERFACE_NUM] = { 0 };
+	int if_id = 0, gap = 0;
+	u32 adll_value = 0;
+	u32 reg;
+	enum hws_access_type pup_access;
+	u32 cs;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (mode == 1) { /* per pup */
+		start_pup = 0;
+		end_pup = octets_per_if_num - 1;
+		pup_access = ACCESS_TYPE_UNICAST;
+	} else {
+		start_pup = 0;
+		end_pup = 0;
+		pup_access = ACCESS_TYPE_MULTICAST;
+	}
+
+	for (cs = 0; cs < max_cs; cs++) {
+		reg = (direction == 0) ? WL_PHY_REG(cs) : RL_PHY_REG(cs);
+		for (adll = 0; adll < ADLL_LENGTH; adll++) {
+			for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				for (pup = start_pup; pup <= end_pup; pup++)
+					ctrl_sweepres[adll][if_id][pup] = 0;
+			}
+		}
+
+		for (adll = 0; adll < MAX_INTERFACE_NUM * MAX_BUS_NUM; adll++) {
+			ctrl_adll[adll] = 0;
+			ctrl_level_phase[adll] = 0;
+			ctrl_adll1[adll] = 0;
+		}
+
+		/* save leveling value after running algorithm */
+		ddr3_tip_read_adll_value(dev_num, ctrl_adll, reg, 0x1f);
+		read_phase_value(dev_num, ctrl_level_phase, reg, 0x7 << 6);
+
+		if (direction == 0)
+			ddr3_tip_read_adll_value(dev_num, ctrl_adll1,
+						 CTX_PHY_REG(cs), MASK_ALL_BITS);
+
+		/* Sweep ADLL from 0 to 31 on all interfaces, all pups,
+		 * and perform BIST on each stage
+		 */
+		for (pup = start_pup; pup <= end_pup; pup++) {
+			for (adll = 0; adll < ADLL_LENGTH; adll++) {
+				for (rep = 0; rep < repeat_num; rep++) {
+					adll_value = (direction == 0) ? (adll * 2) : (adll * 3);
+					for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+						start_adll = ctrl_adll[if_id * cs * octets_per_if_num + pup] +
+							     (ctrl_level_phase[if_id * cs *
+									     octets_per_if_num +
+									     pup] >> 6) * 32;
+
+						if (direction == 0)
+							start_adll = (start_adll > 32) ? (start_adll - 32) : 0;
+						else
+							start_adll = (start_adll > 48) ? (start_adll - 48) : 0;
+
+						adll_value += start_adll;
+
+						gap = ctrl_adll1[if_id * cs * octets_per_if_num + pup] -
+						      ctrl_adll[if_id * cs * octets_per_if_num + pup];
+						gap = (((adll_value % 32) + gap) % 64);
+
+						adll_value = ((adll_value % 32) +
+							       (((adll_value - (adll_value % 32)) / 32) << 6));
+
+						CHECK_STATUS(ddr3_tip_bus_write(dev_num,
+										ACCESS_TYPE_UNICAST,
+										if_id,
+										pup_access,
+										pup,
+										DDR_PHY_DATA,
+										reg,
+										adll_value));
+						if (direction == 0)
+							CHECK_STATUS(ddr3_tip_bus_write(dev_num,
+											ACCESS_TYPE_UNICAST,
+											if_id,
+											pup_access,
+											pup,
+											DDR_PHY_DATA,
+											CTX_PHY_REG(cs),
+											gap));
+					}
+
+					for (pattern_idx = PATTERN_KILLER_DQ0;
+					     pattern_idx < PATTERN_LAST;
+					     pattern_idx++) {
+						hws_ddr3_run_bist(dev_num, sweep_pattern, res, cs);
+						ddr3_tip_reset_fifo_ptr(dev_num);
+						for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+							VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+							if (pup != 4) { /* TODO: remove literal */
+								ctrl_sweepres[adll][if_id][pup] += res[if_id];
+							} else {
+								CHECK_STATUS(ddr3_tip_if_read(dev_num,
+											      ACCESS_TYPE_UNICAST,
+											      if_id,
+											      0x1458,
+											      read_data,
+											      MASK_ALL_BITS));
+								ctrl_sweepres[adll][if_id][pup] += read_data[if_id];
+								CHECK_STATUS(ddr3_tip_if_write(dev_num,
+											       ACCESS_TYPE_UNICAST,
+											       if_id,
+											       0x1458,
+											       0x0,
+											       0xFFFFFFFF));
+								CHECK_STATUS(ddr3_tip_if_write(dev_num,
+											       ACCESS_TYPE_UNICAST,
+											       if_id,
+											       0x145C,
+											       0x0,
+											       0xFFFFFFFF));
+							}
+						}
+					}
+				}
+			}
+
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+				start_adll = ctrl_adll[if_id * cs * octets_per_if_num + pup] +
+					     ctrl_level_phase[if_id * cs * octets_per_if_num + pup];
+				CHECK_STATUS(ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, pup_access, pup,
+								DDR_PHY_DATA, reg, start_adll));
+				if (direction == 0)
+					CHECK_STATUS(ddr3_tip_bus_write(dev_num,
+									ACCESS_TYPE_UNICAST,
+									if_id,
+									pup_access,
+									pup,
+									DDR_PHY_DATA,
+									CTX_PHY_REG(cs),
+									ctrl_adll1[if_id *
+										   cs *
+										   octets_per_if_num +
+										   pup]));
+			}
+		}
+
+		printf("Final,CS %d,%s,Leveling,Result,Adll,", cs, ((direction == 0) ? "TX" : "RX"));
+
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			if (mode == 1) {
+				for (pup = start_pup; pup <= end_pup; pup++) {
+					VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+					printf("I/F%d-PHY%d , ", if_id, pup);
+				}
+			} else {
+				printf("I/F%d , ", if_id);
+			}
+		}
+		printf("\n");
+
+		for (adll = 0; adll < ADLL_LENGTH; adll++) {
+			adll_value = (direction == 0) ? ((adll * 2) - 32) : ((adll * 3) - 48);
+			printf("Final,%s,LevelingSweep,Result, %d ,", ((direction == 0) ? "TX" : "RX"), adll_value);
+
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				for (pup = start_pup; pup <= end_pup; pup++)
+					printf("%8d , ", ctrl_sweepres[adll][if_id][pup]);
+			}
+			printf("\n");
+		}
+
+		/* write back to the phy the Rx DQS value, we store in the beginning */
+		write_leveling_value(dev_num, ctrl_adll, ctrl_level_phase, reg);
+		if (direction == 0)
+			ddr3_tip_write_adll_value(dev_num, ctrl_adll1, CTX_PHY_REG(cs));
+
+		/* print adll results */
+		ddr3_tip_read_adll_value(dev_num, ctrl_adll, reg, MASK_ALL_BITS);
+		printf("%s,DQS,Leveling,,,", (direction == 0) ? "Tx" : "Rx");
+		print_adll(dev_num, ctrl_adll);
+		print_ph(dev_num, ctrl_level_phase);
+	}
+	ddr3_tip_reset_fifo_ptr(dev_num);
+
+	return 0;
+}
+#endif /* EXCLUDE_SWITCH_DEBUG */
+
+void print_topology(struct mv_ddr_topology_map *topology_db)
+{
+	u32 ui, uj;
+	u32 dev_num = 0;
+
+	printf("\tinterface_mask: 0x%x\n", topology_db->if_act_mask);
+	printf("\tNumber of buses: 0x%x\n",
+	       ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE));
+	printf("\tbus_act_mask: 0x%x\n", topology_db->bus_act_mask);
+
+	for (ui = 0; ui < MAX_INTERFACE_NUM; ui++) {
+		VALIDATE_IF_ACTIVE(topology_db->if_act_mask, ui);
+		printf("\n\tInterface ID: %d\n", ui);
+		printf("\t\tDDR Frequency: %s\n",
+		       convert_freq(topology_db->
+				    interface_params[ui].memory_freq));
+		printf("\t\tSpeed_bin: %d\n",
+		       topology_db->interface_params[ui].speed_bin_index);
+		printf("\t\tBus_width: %d\n",
+		       (4 << topology_db->interface_params[ui].bus_width));
+		printf("\t\tMem_size: %s\n",
+		       convert_mem_size(topology_db->
+					interface_params[ui].memory_size));
+		printf("\t\tCAS-WL: %d\n",
+		       topology_db->interface_params[ui].cas_wl);
+		printf("\t\tCAS-L: %d\n",
+		       topology_db->interface_params[ui].cas_l);
+		printf("\t\tTemperature: %d\n",
+		       topology_db->interface_params[ui].interface_temp);
+		printf("\n");
+		for (uj = 0; uj < 4; uj++) {
+			printf("\t\tBus %d parameters- CS Mask: 0x%x\t", uj,
+			       topology_db->interface_params[ui].
+			       as_bus_params[uj].cs_bitmask);
+			printf("Mirror: 0x%x\t",
+			       topology_db->interface_params[ui].
+			       as_bus_params[uj].mirror_enable_bitmask);
+			printf("DQS Swap is %s \t",
+			       (topology_db->
+				interface_params[ui].as_bus_params[uj].
+				is_dqs_swap == 1) ? "enabled" : "disabled");
+			printf("Ck Swap:%s\t",
+			       (topology_db->
+				interface_params[ui].as_bus_params[uj].
+				is_ck_swap == 1) ? "enabled" : "disabled");
+			printf("\n");
+		}
+	}
+}
+#endif /* DDR_VIEWER_TOOL */
+
+#if !defined(EXCLUDE_SWITCH_DEBUG)
+/*
+ * Execute XSB Test transaction (rd/wr/both)
+ */
+int run_xsb_test(u32 dev_num, u32 mem_addr, u32 write_type,
+		 u32 read_type, u32 burst_length)
+{
+	u32 seq = 0, if_id = 0, addr, cnt;
+	int ret = MV_OK, ret_tmp;
+	u32 data_read[MAX_INTERFACE_NUM];
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		addr = mem_addr;
+		for (cnt = 0; cnt <= burst_length; cnt++) {
+			seq = (seq + 1) % 8;
+			if (write_type != 0) {
+				CHECK_STATUS(ddr3_tip_ext_write
+					     (dev_num, if_id, addr, 1,
+					      xsb_test_table[seq]));
+			}
+			if (read_type != 0) {
+				CHECK_STATUS(ddr3_tip_ext_read
+					     (dev_num, if_id, addr, 1,
+					      data_read));
+			}
+			if ((read_type != 0) && (write_type != 0)) {
+				ret_tmp =
+					ddr3_tip_compare(if_id,
+							 xsb_test_table[seq],
+							 data_read,
+							 0xff);
+				addr += (EXT_ACCESS_BURST_LENGTH * 4);
+				ret = (ret != MV_OK) ? ret : ret_tmp;
+			}
+		}
+	}
+
+	return ret;
+}
+
+#else /*EXCLUDE_SWITCH_DEBUG */
+u32 start_xsb_offset = 0;
+
+int run_xsb_test(u32 dev_num, u32 mem_addr, u32 write_type,
+		 u32 read_type, u32 burst_length)
+{
+	return MV_OK;
+}
+
+#endif /* EXCLUDE_SWITCH_DEBUG */
diff --git a/drivers/ddr/marvell/a38x/ddr3_init.c b/drivers/ddr/marvell/a38x/ddr3_init.c
new file mode 100644
index 0000000..22c8f9c
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_init.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_common.h"
+
+static char *ddr_type = "DDR3";
+
+/*
+ * generic_init_controller controls D-unit configuration:
+ * '1' - dynamic D-unit configuration,
+ */
+u8 generic_init_controller = 1;
+
+static int mv_ddr_training_params_set(u8 dev_num);
+
+/*
+ * Name:     ddr3_init - Main DDR3 Init function
+ * Desc:     This routine initialize the DDR3 MC and runs HW training.
+ * Args:     None.
+ * Notes:
+ * Returns:  None.
+ */
+int ddr3_init(void)
+{
+	int status;
+	int is_manual_cal_done;
+
+	/* Print mv_ddr version */
+	mv_ddr_ver_print();
+
+	mv_ddr_pre_training_fixup();
+
+	/* SoC/Board special initializations */
+	mv_ddr_pre_training_soc_config(ddr_type);
+
+	/* Set log level for training library */
+	mv_ddr_user_log_level_set(DEBUG_BLOCK_ALL);
+
+	mv_ddr_early_init();
+
+	if (mv_ddr_topology_map_update()) {
+		printf("mv_ddr: failed to update topology\n");
+		return MV_FAIL;
+	}
+
+	if (mv_ddr_early_init2() != MV_OK)
+		return MV_FAIL;
+
+	/* Set training algorithm's parameters */
+	status = mv_ddr_training_params_set(0);
+	if (MV_OK != status)
+		return status;
+
+	mv_ddr_mc_config();
+
+	is_manual_cal_done = mv_ddr_manual_cal_do();
+
+	mv_ddr_mc_init();
+
+	if (!is_manual_cal_done) {
+	}
+
+
+	status = ddr3_silicon_post_init();
+	if (MV_OK != status) {
+		printf("DDR3 Post Init - FAILED 0x%x\n", status);
+		return status;
+	}
+
+	/* PHY initialization (Training) */
+	status = hws_ddr3_tip_run_alg(0, ALGO_TYPE_DYNAMIC);
+	if (MV_OK != status) {
+		printf("%s Training Sequence - FAILED\n", ddr_type);
+		return status;
+	}
+
+#if defined(CONFIG_PHY_STATIC_PRINT)
+	mv_ddr_phy_static_print();
+#endif
+
+	/* Post MC/PHY initializations */
+	mv_ddr_post_training_soc_config(ddr_type);
+
+	mv_ddr_post_training_fixup();
+
+	if (mv_ddr_is_ecc_ena())
+		mv_ddr_mem_scrubbing();
+
+	printf("mv_ddr: completed successfully\n");
+
+	return MV_OK;
+}
+
+/*
+ * Name:	mv_ddr_training_params_set
+ * Desc:
+ * Args:
+ * Notes:	sets internal training params
+ * Returns:
+ */
+static int mv_ddr_training_params_set(u8 dev_num)
+{
+	struct tune_train_params params;
+	int status;
+	u32 cs_num;
+
+	cs_num = mv_ddr_cs_num_get();
+
+	/* NOTE: do not remove any field initilization */
+	params.ck_delay = TUNE_TRAINING_PARAMS_CK_DELAY;
+	params.phy_reg3_val = TUNE_TRAINING_PARAMS_PHYREG3VAL;
+	params.g_zpri_data = TUNE_TRAINING_PARAMS_PRI_DATA;
+	params.g_znri_data = TUNE_TRAINING_PARAMS_NRI_DATA;
+	params.g_zpri_ctrl = TUNE_TRAINING_PARAMS_PRI_CTRL;
+	params.g_znri_ctrl = TUNE_TRAINING_PARAMS_NRI_CTRL;
+	params.g_znodt_data = TUNE_TRAINING_PARAMS_N_ODT_DATA;
+	params.g_zpodt_ctrl = TUNE_TRAINING_PARAMS_P_ODT_CTRL;
+	params.g_znodt_ctrl = TUNE_TRAINING_PARAMS_N_ODT_CTRL;
+
+	params.g_zpodt_data = TUNE_TRAINING_PARAMS_P_ODT_DATA;
+	params.g_dic = TUNE_TRAINING_PARAMS_DIC;
+	params.g_rtt_nom = TUNE_TRAINING_PARAMS_RTT_NOM;
+	if (cs_num == 1) {
+		params.g_rtt_wr = TUNE_TRAINING_PARAMS_RTT_WR_1CS;
+		params.g_odt_config = TUNE_TRAINING_PARAMS_ODT_CONFIG_1CS;
+	} else {
+		params.g_rtt_wr = TUNE_TRAINING_PARAMS_RTT_WR_2CS;
+		params.g_odt_config = TUNE_TRAINING_PARAMS_ODT_CONFIG_2CS;
+	}
+
+	status = ddr3_tip_tune_training_params(dev_num, &params);
+	if (MV_OK != status) {
+		printf("%s Training Sequence - FAILED\n", ddr_type);
+		return status;
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_init.h b/drivers/ddr/marvell/a38x/ddr3_init.h
new file mode 100644
index 0000000..055516b
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_init.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_INIT_H
+#define _DDR3_INIT_H
+
+#include "ddr_ml_wrapper.h"
+#include "mv_ddr_plat.h"
+
+#include "seq_exec.h"
+#include "ddr3_logging_def.h"
+#include "ddr3_training_hw_algo.h"
+#include "ddr3_training_ip.h"
+#include "ddr3_training_ip_centralization.h"
+#include "ddr3_training_ip_engine.h"
+#include "ddr3_training_ip_flow.h"
+#include "ddr3_training_ip_pbs.h"
+#include "ddr3_training_ip_prv_if.h"
+#include "ddr3_training_leveling.h"
+#include "xor.h"
+
+/* For checking function return values */
+#define CHECK_STATUS(orig_func)		\
+	{				\
+		int status;		\
+		status = orig_func;	\
+		if (MV_OK != status)	\
+			return status;	\
+	}
+
+#define SUB_VERSION	0
+
+enum log_level  {
+	MV_LOG_LEVEL_0,
+	MV_LOG_LEVEL_1,
+	MV_LOG_LEVEL_2,
+	MV_LOG_LEVEL_3
+};
+
+/* TODO: consider to move to misl phy driver */
+#define MISL_PHY_DRV_P_OFFS	0x7
+#define MISL_PHY_DRV_N_OFFS	0x0
+#define MISL_PHY_ODT_P_OFFS	0x6
+#define MISL_PHY_ODT_N_OFFS	0x0
+
+/* Globals */
+extern u8 debug_training, debug_calibration, debug_ddr4_centralization,
+	debug_tap_tuning, debug_dm_tuning;
+extern u8 is_reg_dump;
+extern u8 generic_init_controller;
+/* list of allowed frequency listed in order of enum mv_ddr_freq */
+extern u32 is_pll_old;
+extern struct pattern_info pattern_table[];
+extern u8 debug_centralization, debug_training_ip, debug_training_bist,
+	debug_pbs, debug_training_static, debug_leveling;
+extern struct hws_tip_config_func_db config_func_info[];
+extern u8 twr_mask_table[];
+extern u8 cl_mask_table[];
+extern u8 cwl_mask_table[];
+extern u32 speed_bin_table_t_rc[];
+extern u32 speed_bin_table_t_rcd_t_rp[];
+
+extern u32 vref_init_val;
+extern u32 g_zpri_data;
+extern u32 g_znri_data;
+extern u32 g_zpri_ctrl;
+extern u32 g_znri_ctrl;
+extern u32 g_zpodt_data;
+extern u32 g_znodt_data;
+extern u32 g_zpodt_ctrl;
+extern u32 g_znodt_ctrl;
+extern u32 g_dic;
+extern u32 g_odt_config;
+extern u32 g_rtt_nom;
+extern u32 g_rtt_wr;
+extern u32 g_rtt_park;
+
+extern u8 debug_training_access;
+extern u32 first_active_if;
+extern u32 delay_enable, ck_delay, ca_delay;
+extern u32 mask_tune_func;
+extern u32 rl_version;
+extern int rl_mid_freq_wa;
+extern u8 calibration_update_control; /* 2 external only, 1 is internal only */
+extern enum mv_ddr_freq medium_freq;
+
+extern enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM];
+extern enum mv_ddr_freq low_freq;
+extern enum auto_tune_stage training_stage;
+extern u32 is_pll_before_init;
+extern u32 is_adll_calib_before_init;
+extern u32 is_dfs_in_init;
+extern int wl_debug_delay;
+extern u32 silicon_delay[MAX_DEVICE_NUM];
+extern u32 start_pattern, end_pattern;
+extern u32 phy_reg0_val;
+extern u32 phy_reg1_val;
+extern u32 phy_reg2_val;
+extern u32 phy_reg3_val;
+extern enum hws_pattern sweep_pattern;
+extern enum hws_pattern pbs_pattern;
+extern u32 g_znri_data;
+extern u32 g_zpri_data;
+extern u32 g_znri_ctrl;
+extern u32 g_zpri_ctrl;
+extern u32 finger_test, p_finger_start, p_finger_end, n_finger_start,
+	n_finger_end, p_finger_step, n_finger_step;
+extern u32 mode_2t;
+extern u32 xsb_validate_type;
+extern u32 xsb_validation_base_address;
+extern u32 odt_additional;
+extern u32 debug_mode;
+extern u32 debug_dunit;
+extern u32 clamp_tbl[];
+extern u32 freq_mask[MAX_DEVICE_NUM][MV_DDR_FREQ_LAST];
+
+extern u32 maxt_poll_tries;
+extern u32 is_bist_reset_bit;
+
+extern u8 vref_window_size[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+extern u32 effective_cs;
+extern int ddr3_tip_centr_skip_min_win_check;
+extern u32 *dq_map_table;
+
+extern u8 debug_training_hw_alg;
+
+extern u32 start_xsb_offset;
+extern u32 odt_config;
+
+extern u16 mask_results_dq_reg_map[];
+
+extern u32 target_freq;
+extern u32 dfs_low_freq;
+
+extern u32 nominal_avs;
+extern u32 extension_avs;
+
+
+/* Prototypes */
+int ddr3_init(void);
+int ddr3_tip_enable_init_sequence(u32 dev_num);
+
+int ddr3_hws_hw_training(enum hws_algo_type algo_mode);
+int mv_ddr_early_init(void);
+int mv_ddr_early_init2(void);
+int ddr3_silicon_post_init(void);
+int ddr3_post_run_alg(void);
+void ddr3_new_tip_ecc_scrub(void);
+
+int ddr3_tip_reg_write(u32 dev_num, u32 reg_addr, u32 data);
+int ddr3_tip_reg_read(u32 dev_num, u32 reg_addr, u32 *data, u32 reg_mask);
+int ddr3_silicon_get_ddr_target_freq(u32 *ddr_freq);
+
+int print_adll(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM]);
+int print_ph(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM]);
+int read_phase_value(u32 dev_num, u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM],
+		     int reg_addr, u32 mask);
+int write_leveling_value(u32 dev_num, u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM],
+			 u32 pup_ph_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], int reg_addr);
+int ddr3_tip_restore_dunit_regs(u32 dev_num);
+void print_topology(struct mv_ddr_topology_map *tm);
+
+u32 mv_board_id_get(void);
+
+int ddr3_load_topology_map(void);
+void ddr3_hws_set_log_level(enum ddr_lib_debug_block block, u8 level);
+void mv_ddr_user_log_level_set(enum ddr_lib_debug_block block);
+int ddr3_tip_tune_training_params(u32 dev_num,
+				  struct tune_train_params *params);
+void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps);
+void ddr3_fast_path_static_cs_size_config(u32 cs_ena);
+u32 mv_board_id_index_get(u32 board_id);
+void ddr3_set_log_level(u32 n_log_level);
+
+int hws_ddr3_cs_base_adr_calc(u32 if_id, u32 cs, u32 *cs_base_addr);
+
+int ddr3_tip_print_pbs_result(u32 dev_num, u32 cs_num, enum pbs_dir pbs_mode);
+int ddr3_tip_clean_pbs_result(u32 dev_num, enum pbs_dir pbs_mode);
+void mv_ddr_mc_config(void);
+int mv_ddr_mc_init(void);
+void mv_ddr_set_calib_controller(void);
+/* TODO: consider to move to misl phy driver */
+unsigned int mv_ddr_misl_phy_drv_data_p_get(void);
+unsigned int mv_ddr_misl_phy_drv_data_n_get(void);
+unsigned int mv_ddr_misl_phy_drv_ctrl_p_get(void);
+unsigned int mv_ddr_misl_phy_drv_ctrl_n_get(void);
+unsigned int mv_ddr_misl_phy_odt_p_get(void);
+unsigned int mv_ddr_misl_phy_odt_n_get(void);
+
+#endif /* _DDR3_INIT_H */
diff --git a/drivers/ddr/marvell/a38x/ddr3_logging_def.h b/drivers/ddr/marvell/a38x/ddr3_logging_def.h
new file mode 100644
index 0000000..ad9da1c
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_logging_def.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_LOGGING_CONFIG_H
+#define _DDR3_LOGGING_CONFIG_H
+
+#ifdef SILENT_LIB
+#define DEBUG_TRAINING_BIST_ENGINE(level, s)
+#define DEBUG_TRAINING_IP(level, s)
+#define DEBUG_CENTRALIZATION_ENGINE(level, s)
+#define DEBUG_TRAINING_HW_ALG(level, s)
+#define DEBUG_TRAINING_IP_ENGINE(level, s)
+#define DEBUG_LEVELING(level, s)
+#define DEBUG_PBS_ENGINE(level, s)
+#define DEBUG_TRAINING_STATIC_IP(level, s)
+#define DEBUG_TRAINING_ACCESS(level, s)
+#else
+#ifdef LIB_FUNCTIONAL_DEBUG_ONLY
+#define DEBUG_TRAINING_BIST_ENGINE(level, s)
+#define DEBUG_TRAINING_IP_ENGINE(level, s)
+#define DEBUG_TRAINING_IP(level, s)		\
+	if (level >= debug_training)		\
+		printf s
+#define DEBUG_CENTRALIZATION_ENGINE(level, s)	\
+	if (level >= debug_centralization)	\
+		printf s
+#define DEBUG_TRAINING_HW_ALG(level, s)		\
+	if (level >= debug_training_hw_alg)	\
+		printf s
+#define DEBUG_LEVELING(level, s)		\
+	if (level >= debug_leveling)		\
+		printf s
+#define DEBUG_PBS_ENGINE(level, s)		\
+	if (level >= debug_pbs)			\
+		printf s
+#define DEBUG_TRAINING_STATIC_IP(level, s)	\
+	if (level >= debug_training_static)	\
+		printf s
+#define DEBUG_TRAINING_ACCESS(level, s)		\
+	if (level >= debug_training_access)	\
+		printf s
+#else
+#define DEBUG_TRAINING_BIST_ENGINE(level, s)	\
+	if (level >= debug_training_bist)	\
+		printf s
+
+#define DEBUG_TRAINING_IP_ENGINE(level, s)	\
+	if (level >= debug_training_ip)		\
+		printf s
+#define DEBUG_TRAINING_IP(level, s)		\
+	if (level >= debug_training)		\
+		printf s
+#define DEBUG_CENTRALIZATION_ENGINE(level, s)	\
+	if (level >= debug_centralization)	\
+		printf s
+#define DEBUG_TRAINING_HW_ALG(level, s)		\
+	if (level >= debug_training_hw_alg)	\
+		printf s
+#define DEBUG_LEVELING(level, s)		\
+	if (level >= debug_leveling)		\
+		printf s
+#define DEBUG_PBS_ENGINE(level, s)		\
+	if (level >= debug_pbs)			\
+		printf s
+#define DEBUG_TRAINING_STATIC_IP(level, s)	\
+	if (level >= debug_training_static)	\
+		printf s
+#define DEBUG_TRAINING_ACCESS(level, s)		\
+	if (level >= debug_training_access)	\
+		printf s
+#endif
+#endif
+
+
+/* Logging defines */
+enum mv_ddr_debug_level {
+	DEBUG_LEVEL_TRACE = 1,
+	DEBUG_LEVEL_INFO = 2,
+	DEBUG_LEVEL_ERROR = 3,
+	DEBUG_LEVEL_LAST
+};
+
+enum ddr_lib_debug_block {
+	DEBUG_BLOCK_STATIC,
+	DEBUG_BLOCK_TRAINING_MAIN,
+	DEBUG_BLOCK_LEVELING,
+	DEBUG_BLOCK_CENTRALIZATION,
+	DEBUG_BLOCK_PBS,
+	DEBUG_BLOCK_IP,
+	DEBUG_BLOCK_BIST,
+	DEBUG_BLOCK_ALG,
+	DEBUG_BLOCK_DEVICE,
+	DEBUG_BLOCK_ACCESS,
+	DEBUG_STAGES_REG_DUMP,
+	/* All excluding IP and REG_DUMP, should be enabled separatelly */
+	DEBUG_BLOCK_ALL
+};
+
+int ddr3_tip_print_log(u32 dev_num, u32 mem_addr);
+int ddr3_tip_print_stability_log(u32 dev_num);
+
+#endif /* _DDR3_LOGGING_CONFIG_H */
diff --git a/drivers/ddr/marvell/a38x/ddr3_patterns_64bit.h b/drivers/ddr/marvell/a38x/ddr3_patterns_64bit.h
new file mode 100644
index 0000000..1e2260b
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_patterns_64bit.h
@@ -0,0 +1,924 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __DDR3_PATTERNS_64_H
+#define __DDR3_PATTERNS_64_H
+
+#define FAB_OPT		21
+/*
+ * Patterns Declerations
+ */
+
+u32 wl_sup_pattern[LEN_WL_SUP_PATTERN] __aligned(32) = {
+	0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d,
+	0x14131211, 0x18171615, 0x1c1b1a19, 0x201f1e1d,
+	0x24232221, 0x28272625, 0x2c2b2a29, 0x302f2e2d,
+	0x34333231, 0x38373635, 0x3c3b3a39, 0x403f3e3d,
+	0x44434241, 0x48474645, 0x4c4b4a49, 0x504f4e4d,
+	0x54535251, 0x58575655, 0x5c5b5a59, 0x605f5e5d,
+	0x64636261, 0x68676665, 0x6c6b6a69, 0x706f6e6d,
+	0x74737271, 0x78777675, 0x7c7b7a79, 0x807f7e7d
+};
+
+u32 pbs_pattern_32b[2][LEN_PBS_PATTERN] __aligned(32) = {
+	{
+		0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555,
+		0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555,
+		0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555,
+		0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555
+	},
+	{
+		0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa,
+		0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa,
+		0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa,
+		0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa
+	}
+};
+
+u32 pbs_pattern_64b[2][LEN_PBS_PATTERN] __aligned(32) = {
+	{
+		0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555,
+		0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555,
+		0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555,
+		0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555
+	},
+	{
+		0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa,
+		0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa,
+		0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa,
+		0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa
+	}
+};
+
+u32 rl_pattern[LEN_STD_PATTERN] __aligned(32) = {
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x01010101, 0x01010101, 0x01010101, 0x01010101
+};
+
+u32 killer_pattern_32b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = {
+	{
+		0x01010101, 0x00000000, 0x01010101, 0xffffffff,
+		0x01010101, 0x00000000, 0x01010101, 0xffffffff,
+		0xfefefefe, 0xfefefefe, 0x01010101, 0xfefefefe,
+		0xfefefefe, 0xfefefefe, 0x01010101, 0xfefefefe,
+		0x01010101, 0xfefefefe, 0x01010101, 0x01010101,
+		0x01010101, 0xfefefefe, 0x01010101, 0x01010101,
+		0xfefefefe, 0x01010101, 0xfefefefe, 0x00000000,
+		0xfefefefe, 0x01010101, 0xfefefefe, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x01010101,
+		0xffffffff, 0x00000000, 0xffffffff, 0x01010101,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xfefefefe,
+		0x00000000, 0x00000000, 0x00000000, 0xfefefefe,
+		0xfefefefe, 0xffffffff, 0x00000000, 0x00000000,
+		0xfefefefe, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xfefefefe, 0x00000000, 0xfefefefe, 0x00000000,
+		0xfefefefe, 0x00000000, 0xfefefefe, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x01010101,
+		0x00000000, 0xffffffff, 0xffffffff, 0x01010101,
+		0xffffffff, 0xffffffff, 0x01010101, 0x00000000,
+		0xffffffff, 0xffffffff, 0x01010101, 0x00000000,
+		0x01010101, 0xffffffff, 0xfefefefe, 0xfefefefe,
+		0x01010101, 0xffffffff, 0xfefefefe, 0xfefefefe
+	},
+	{
+		0x02020202, 0x00000000, 0x02020202, 0xffffffff,
+		0x02020202, 0x00000000, 0x02020202, 0xffffffff,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0xfdfdfdfd,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0xfdfdfdfd,
+		0x02020202, 0xfdfdfdfd, 0x02020202, 0x02020202,
+		0x02020202, 0xfdfdfdfd, 0x02020202, 0x02020202,
+		0xfdfdfdfd, 0x02020202, 0xfdfdfdfd, 0x00000000,
+		0xfdfdfdfd, 0x02020202, 0xfdfdfdfd, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x02020202,
+		0xffffffff, 0x00000000, 0xffffffff, 0x02020202,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xfdfdfdfd,
+		0x00000000, 0x00000000, 0x00000000, 0xfdfdfdfd,
+		0xfdfdfdfd, 0xffffffff, 0x00000000, 0x00000000,
+		0xfdfdfdfd, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xfdfdfdfd, 0x00000000, 0xfdfdfdfd, 0x00000000,
+		0xfdfdfdfd, 0x00000000, 0xfdfdfdfd, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x02020202,
+		0x00000000, 0xffffffff, 0xffffffff, 0x02020202,
+		0xffffffff, 0xffffffff, 0x02020202, 0x00000000,
+		0xffffffff, 0xffffffff, 0x02020202, 0x00000000,
+		0x02020202, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x02020202, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd
+	},
+	{
+		0x04040404, 0x00000000, 0x04040404, 0xffffffff,
+		0x04040404, 0x00000000, 0x04040404, 0xffffffff,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0xfbfbfbfb,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0xfbfbfbfb,
+		0x04040404, 0xfbfbfbfb, 0x04040404, 0x04040404,
+		0x04040404, 0xfbfbfbfb, 0x04040404, 0x04040404,
+		0xfbfbfbfb, 0x04040404, 0xfbfbfbfb, 0x00000000,
+		0xfbfbfbfb, 0x04040404, 0xfbfbfbfb, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x04040404,
+		0xffffffff, 0x00000000, 0xffffffff, 0x04040404,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xfbfbfbfb,
+		0x00000000, 0x00000000, 0x00000000, 0xfbfbfbfb,
+		0xfbfbfbfb, 0xffffffff, 0x00000000, 0x00000000,
+		0xfbfbfbfb, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xfbfbfbfb, 0x00000000, 0xfbfbfbfb, 0x00000000,
+		0xfbfbfbfb, 0x00000000, 0xfbfbfbfb, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x04040404,
+		0x00000000, 0xffffffff, 0xffffffff, 0x04040404,
+		0xffffffff, 0xffffffff, 0x04040404, 0x00000000,
+		0xffffffff, 0xffffffff, 0x04040404, 0x00000000,
+		0x04040404, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x04040404, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb
+	},
+	{
+		0x08080808, 0x00000000, 0x08080808, 0xffffffff,
+		0x08080808, 0x00000000, 0x08080808, 0xffffffff,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0xf7f7f7f7,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0xf7f7f7f7,
+		0x08080808, 0xf7f7f7f7, 0x08080808, 0x08080808,
+		0x08080808, 0xf7f7f7f7, 0x08080808, 0x08080808,
+		0xf7f7f7f7, 0x08080808, 0xf7f7f7f7, 0x00000000,
+		0xf7f7f7f7, 0x08080808, 0xf7f7f7f7, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x08080808,
+		0xffffffff, 0x00000000, 0xffffffff, 0x08080808,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xf7f7f7f7,
+		0x00000000, 0x00000000, 0x00000000, 0xf7f7f7f7,
+		0xf7f7f7f7, 0xffffffff, 0x00000000, 0x00000000,
+		0xf7f7f7f7, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xf7f7f7f7, 0x00000000, 0xf7f7f7f7, 0x00000000,
+		0xf7f7f7f7, 0x00000000, 0xf7f7f7f7, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x08080808,
+		0x00000000, 0xffffffff, 0xffffffff, 0x08080808,
+		0xffffffff, 0xffffffff, 0x08080808, 0x00000000,
+		0xffffffff, 0xffffffff, 0x08080808, 0x00000000,
+		0x08080808, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x08080808, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7
+	},
+	{
+		0x10101010, 0x00000000, 0x10101010, 0xffffffff,
+		0x10101010, 0x00000000, 0x10101010, 0xffffffff,
+		0xefefefef, 0xefefefef, 0x10101010, 0xefefefef,
+		0xefefefef, 0xefefefef, 0x10101010, 0xefefefef,
+		0x10101010, 0xefefefef, 0x10101010, 0x10101010,
+		0x10101010, 0xefefefef, 0x10101010, 0x10101010,
+		0xefefefef, 0x10101010, 0xefefefef, 0x00000000,
+		0xefefefef, 0x10101010, 0xefefefef, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x10101010,
+		0xffffffff, 0x00000000, 0xffffffff, 0x10101010,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xefefefef,
+		0x00000000, 0x00000000, 0x00000000, 0xefefefef,
+		0xefefefef, 0xffffffff, 0x00000000, 0x00000000,
+		0xefefefef, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xefefefef, 0x00000000, 0xefefefef, 0x00000000,
+		0xefefefef, 0x00000000, 0xefefefef, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x10101010,
+		0x00000000, 0xffffffff, 0xffffffff, 0x10101010,
+		0xffffffff, 0xffffffff, 0x10101010, 0x00000000,
+		0xffffffff, 0xffffffff, 0x10101010, 0x00000000,
+		0x10101010, 0xffffffff, 0xefefefef, 0xefefefef,
+		0x10101010, 0xffffffff, 0xefefefef, 0xefefefef
+	},
+	{
+		0x20202020, 0x00000000, 0x20202020, 0xffffffff,
+		0x20202020, 0x00000000, 0x20202020, 0xffffffff,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0xdfdfdfdf,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0xdfdfdfdf,
+		0x20202020, 0xdfdfdfdf, 0x20202020, 0x20202020,
+		0x20202020, 0xdfdfdfdf, 0x20202020, 0x20202020,
+		0xdfdfdfdf, 0x20202020, 0xdfdfdfdf, 0x00000000,
+		0xdfdfdfdf, 0x20202020, 0xdfdfdfdf, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x20202020,
+		0xffffffff, 0x00000000, 0xffffffff, 0x20202020,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xdfdfdfdf,
+		0x00000000, 0x00000000, 0x00000000, 0xdfdfdfdf,
+		0xdfdfdfdf, 0xffffffff, 0x00000000, 0x00000000,
+		0xdfdfdfdf, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xdfdfdfdf, 0x00000000, 0xdfdfdfdf, 0x00000000,
+		0xdfdfdfdf, 0x00000000, 0xdfdfdfdf, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x20202020,
+		0x00000000, 0xffffffff, 0xffffffff, 0x20202020,
+		0xffffffff, 0xffffffff, 0x20202020, 0x00000000,
+		0xffffffff, 0xffffffff, 0x20202020, 0x00000000,
+		0x20202020, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x20202020, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf
+	},
+	{
+		0x40404040, 0x00000000, 0x40404040, 0xffffffff,
+		0x40404040, 0x00000000, 0x40404040, 0xffffffff,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0xbfbfbfbf,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0xbfbfbfbf,
+		0x40404040, 0xbfbfbfbf, 0x40404040, 0x40404040,
+		0x40404040, 0xbfbfbfbf, 0x40404040, 0x40404040,
+		0xbfbfbfbf, 0x40404040, 0xbfbfbfbf, 0x00000000,
+		0xbfbfbfbf, 0x40404040, 0xbfbfbfbf, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x40404040,
+		0xffffffff, 0x00000000, 0xffffffff, 0x40404040,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0xbfbfbfbf,
+		0x00000000, 0x00000000, 0x00000000, 0xbfbfbfbf,
+		0xbfbfbfbf, 0xffffffff, 0x00000000, 0x00000000,
+		0xbfbfbfbf, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0xbfbfbfbf, 0x00000000, 0xbfbfbfbf, 0x00000000,
+		0xbfbfbfbf, 0x00000000, 0xbfbfbfbf, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x40404040,
+		0x00000000, 0xffffffff, 0xffffffff, 0x40404040,
+		0xffffffff, 0xffffffff, 0x40404040, 0x00000000,
+		0xffffffff, 0xffffffff, 0x40404040, 0x00000000,
+		0x40404040, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x40404040, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf
+	},
+	{
+		0x80808080, 0x00000000, 0x80808080, 0xffffffff,
+		0x80808080, 0x00000000, 0x80808080, 0xffffffff,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x7f7f7f7f,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x7f7f7f7f,
+		0x80808080, 0x7f7f7f7f, 0x80808080, 0x80808080,
+		0x80808080, 0x7f7f7f7f, 0x80808080, 0x80808080,
+		0x7f7f7f7f, 0x80808080, 0x7f7f7f7f, 0x00000000,
+		0x7f7f7f7f, 0x80808080, 0x7f7f7f7f, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x00000000,
+		0xffffffff, 0x00000000, 0xffffffff, 0x80808080,
+		0xffffffff, 0x00000000, 0xffffffff, 0x80808080,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x7f7f7f7f,
+		0x00000000, 0x00000000, 0x00000000, 0x7f7f7f7f,
+		0x7f7f7f7f, 0xffffffff, 0x00000000, 0x00000000,
+		0x7f7f7f7f, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x00000000, 0xffffffff, 0x00000000, 0xffffffff,
+		0x7f7f7f7f, 0x00000000, 0x7f7f7f7f, 0x00000000,
+		0x7f7f7f7f, 0x00000000, 0x7f7f7f7f, 0x00000000,
+		0x00000000, 0xffffffff, 0xffffffff, 0x80808080,
+		0x00000000, 0xffffffff, 0xffffffff, 0x80808080,
+		0xffffffff, 0xffffffff, 0x80808080, 0x00000000,
+		0xffffffff, 0xffffffff, 0x80808080, 0x00000000,
+		0x80808080, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x80808080, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f
+	}
+};
+
+u32 killer_pattern_64b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = {
+	{
+		0x01010101, 0x01010101, 0x00000000, 0x00000000,
+		0x01010101, 0x01010101, 0xffffffff, 0xffffffff,
+		0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe,
+		0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe,
+		0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe,
+		0x01010101, 0x01010101, 0x01010101, 0x01010101,
+		0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101,
+		0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x01010101, 0x01010101,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xfefefefe, 0xfefefefe,
+		0xfefefefe, 0xfefefefe, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000,
+		0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x01010101, 0x01010101,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x01010101, 0x01010101, 0x00000000, 0x00000000,
+		0x01010101, 0x01010101, 0xffffffff, 0xffffffff,
+		0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe
+	},
+	{
+		0x02020202, 0x02020202, 0x00000000, 0x00000000,
+		0x02020202, 0x02020202, 0xffffffff, 0xffffffff,
+		0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x02020202, 0x02020202, 0x02020202, 0x02020202,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x02020202, 0x02020202,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xfdfdfdfd, 0xfdfdfdfd,
+		0xfdfdfdfd, 0xfdfdfdfd, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x02020202, 0x02020202,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x02020202, 0x02020202, 0x00000000, 0x00000000,
+		0x02020202, 0x02020202, 0xffffffff, 0xffffffff,
+		0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd
+	},
+	{
+		0x04040404, 0x04040404, 0x00000000, 0x00000000,
+		0x04040404, 0x04040404, 0xffffffff, 0xffffffff,
+		0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x04040404, 0x04040404, 0x04040404, 0x04040404,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x04040404, 0x04040404,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xfbfbfbfb, 0xfbfbfbfb,
+		0xfbfbfbfb, 0xfbfbfbfb, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x04040404, 0x04040404,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x04040404, 0x04040404, 0x00000000, 0x00000000,
+		0x04040404, 0x04040404, 0xffffffff, 0xffffffff,
+		0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb
+	},
+	{
+		0x08080808, 0x08080808, 0x00000000, 0x00000000,
+		0x08080808, 0x08080808, 0xffffffff, 0xffffffff,
+		0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x08080808, 0x08080808, 0x08080808, 0x08080808,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x08080808, 0x08080808,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xf7f7f7f7, 0xf7f7f7f7,
+		0xf7f7f7f7, 0xf7f7f7f7, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x08080808, 0x08080808,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x08080808, 0x08080808, 0x00000000, 0x00000000,
+		0x08080808, 0x08080808, 0xffffffff, 0xffffffff,
+		0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7
+	},
+	{
+		0x10101010, 0x10101010, 0x00000000, 0x00000000,
+		0x10101010, 0x10101010, 0xffffffff, 0xffffffff,
+		0xefefefef, 0xefefefef, 0xefefefef, 0xefefefef,
+		0x10101010, 0x10101010, 0xefefefef, 0xefefefef,
+		0x10101010, 0x10101010, 0xefefefef, 0xefefefef,
+		0x10101010, 0x10101010, 0x10101010, 0x10101010,
+		0xefefefef, 0xefefefef, 0x10101010, 0x10101010,
+		0xefefefef, 0xefefefef, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x10101010, 0x10101010,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xefefefef, 0xefefefef,
+		0xefefefef, 0xefefefef, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xefefefef, 0xefefefef, 0x00000000, 0x00000000,
+		0xefefefef, 0xefefefef, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x10101010, 0x10101010,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x10101010, 0x10101010, 0x00000000, 0x00000000,
+		0x10101010, 0x10101010, 0xffffffff, 0xffffffff,
+		0xefefefef, 0xefefefef, 0xefefefef, 0xefefefef
+	},
+	{
+		0x20202020, 0x20202020, 0x00000000, 0x00000000,
+		0x20202020, 0x20202020, 0xffffffff, 0xffffffff,
+		0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x20202020, 0x20202020, 0x20202020, 0x20202020,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x20202020, 0x20202020,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xdfdfdfdf, 0xdfdfdfdf,
+		0xdfdfdfdf, 0xdfdfdfdf, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x20202020, 0x20202020,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x20202020, 0x20202020, 0x00000000, 0x00000000,
+		0x20202020, 0x20202020, 0xffffffff, 0xffffffff,
+		0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf
+	},
+	{
+		0x40404040, 0x40404040, 0x00000000, 0x00000000,
+		0x40404040, 0x40404040, 0xffffffff, 0xffffffff,
+		0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x40404040, 0x40404040, 0x40404040, 0x40404040,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x40404040, 0x40404040,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xbfbfbfbf, 0xbfbfbfbf,
+		0xbfbfbfbf, 0xbfbfbfbf, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x40404040, 0x40404040,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x40404040, 0x40404040, 0x00000000, 0x00000000,
+		0x40404040, 0x40404040, 0xffffffff, 0xffffffff,
+		0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf
+	},
+	{
+		0x80808080, 0x80808080, 0x00000000, 0x00000000,
+		0x80808080, 0x80808080, 0xffffffff, 0xffffffff,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x80808080, 0x80808080, 0x80808080, 0x80808080,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x80808080, 0x80808080,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x7f7f7f7f, 0x7f7f7f7f, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x80808080, 0x80808080,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x80808080, 0x80808080, 0x00000000, 0x00000000,
+		0x80808080, 0x80808080, 0xffffffff, 0xffffffff,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f
+	}
+};
+
+u32 special_pattern[DQ_NUM][LEN_SPECIAL_PATTERN] __aligned(32) = {
+	{
+		0x00000000, 0x00000000, 0x01010101, 0x01010101,
+		0xffffffff, 0xffffffff, 0xfefefefe, 0xfefefefe,
+		0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101,
+		0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101,
+		0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101,
+		0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe,
+		0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x01010101, 0x01010101, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xfefefefe, 0xfefefefe,
+		0x00000000, 0x00000000, 0xfefefefe, 0xfefefefe,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x01010101, 0x01010101, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x01010101, 0x01010101,
+		0x00000000, 0x00000000, 0x01010101, 0x01010101,
+		0xffffffff, 0xffffffff, 0xfefefefe, 0xfefefefe,
+		0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x02020202, 0x02020202,
+		0xffffffff, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202,
+		0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x02020202, 0x02020202, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x00000000, 0x00000000, 0xfdfdfdfd, 0xfdfdfdfd,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x02020202, 0x02020202, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x02020202, 0x02020202,
+		0x00000000, 0x00000000, 0x02020202, 0x02020202,
+		0xffffffff, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd,
+		0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x04040404, 0x04040404,
+		0xffffffff, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404,
+		0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x04040404, 0x04040404, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x00000000, 0x00000000, 0xfbfbfbfb, 0xfbfbfbfb,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x04040404, 0x04040404, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x04040404, 0x04040404,
+		0x00000000, 0x00000000, 0x04040404, 0x04040404,
+		0xffffffff, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb,
+		0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x08080808, 0x08080808,
+		0xffffffff, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808,
+		0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x08080808, 0x08080808, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x00000000, 0x00000000, 0xf7f7f7f7, 0xf7f7f7f7,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x08080808, 0x08080808, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x08080808, 0x08080808,
+		0x00000000, 0x00000000, 0x08080808, 0x08080808,
+		0xffffffff, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7,
+		0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x10101010, 0x10101010,
+		0xffffffff, 0xffffffff, 0xefefefef, 0xefefefef,
+		0xefefefef, 0xefefefef, 0x10101010, 0x10101010,
+		0xefefefef, 0xefefefef, 0x10101010, 0x10101010,
+		0xefefefef, 0xefefefef, 0x10101010, 0x10101010,
+		0x10101010, 0x10101010, 0xefefefef, 0xefefefef,
+		0x10101010, 0x10101010, 0xefefefef, 0xefefefef,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x10101010, 0x10101010, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xefefefef, 0xefefefef, 0xefefefef, 0xefefefef,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xefefefef, 0xefefefef,
+		0x00000000, 0x00000000, 0xefefefef, 0xefefefef,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x10101010, 0x10101010, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x10101010, 0x10101010,
+		0x00000000, 0x00000000, 0x10101010, 0x10101010,
+		0xffffffff, 0xffffffff, 0xefefefef, 0xefefefef,
+		0xefefefef, 0xefefefef, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x20202020, 0x20202020,
+		0xffffffff, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020,
+		0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x20202020, 0x20202020, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x00000000, 0x00000000, 0xdfdfdfdf, 0xdfdfdfdf,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x20202020, 0x20202020, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x20202020, 0x20202020,
+		0x00000000, 0x00000000, 0x20202020, 0x20202020,
+		0xffffffff, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf,
+		0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x40404040, 0x40404040,
+		0xffffffff, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040,
+		0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x40404040, 0x40404040, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x00000000, 0x00000000, 0xbfbfbfbf, 0xbfbfbfbf,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x40404040, 0x40404040, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x40404040, 0x40404040,
+		0x00000000, 0x00000000, 0x40404040, 0x40404040,
+		0xffffffff, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf,
+		0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x80808080, 0x80808080,
+		0xffffffff, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080,
+		0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0x80808080, 0x80808080, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x00000000, 0x00000000, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+		0x80808080, 0x80808080, 0xffffffff, 0xffffffff,
+		0xffffffff, 0xffffffff, 0x80808080, 0x80808080,
+		0x00000000, 0x00000000, 0x80808080, 0x80808080,
+		0xffffffff, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f,
+		0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000
+	}
+};
+
+/* Fabric ratios table */
+u32 fabric_ratio[FAB_OPT] = {
+	0x04010204,
+	0x04020202,
+	0x08020306,
+	0x08020303,
+	0x04020303,
+	0x04020204,
+	0x04010202,
+	0x08030606,
+	0x08030505,
+	0x04020306,
+	0x0804050a,
+	0x04030606,
+	0x04020404,
+	0x04030306,
+	0x04020505,
+	0x08020505,
+	0x04010303,
+	0x08050a0a,
+	0x04030408,
+	0x04010102,
+	0x08030306
+};
+
+u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM] = {
+	{3, 2, 5, 7, 1, 0, 6, 4},
+	{2, 3, 6, 7, 1, 0, 4, 5},
+	{1, 3, 5, 6, 0, 2, 4, 7},
+	{0, 2, 4, 7, 1, 3, 5, 6},
+	{3, 0, 4, 6, 1, 2, 5, 7},
+	{0, 3, 5, 7, 1, 2, 4, 6},
+	{2, 3, 5, 7, 1, 0, 4, 6},
+	{0, 2, 5, 4, 1, 3, 6, 7},
+	{2, 3, 4, 7, 0, 1, 5, 6}
+};
+
+#endif /* __DDR3_PATTERNS_64_H */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training.c b/drivers/ddr/marvell/a38x/ddr3_training.c
new file mode 100644
index 0000000..c7be700
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training.c
@@ -0,0 +1,2896 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_common.h"
+#include "mv_ddr_training_db.h"
+#include "mv_ddr_regs.h"
+
+#define GET_CS_FROM_MASK(mask)	(cs_mask2_num[mask])
+#define CS_CBE_VALUE(cs_num)	(cs_cbe_reg[cs_num])
+
+u32 window_mem_addr = 0;
+u32 phy_reg0_val = 0;
+u32 phy_reg1_val = 8;
+u32 phy_reg2_val = 0;
+u32 phy_reg3_val = PARAM_UNDEFINED;
+enum mv_ddr_freq low_freq = MV_DDR_FREQ_LOW_FREQ;
+enum mv_ddr_freq medium_freq;
+u32 debug_dunit = 0;
+u32 odt_additional = 1;
+u32 *dq_map_table = NULL;
+
+/* in case of ddr4 do not run ddr3_tip_write_additional_odt_setting function - mc odt always 'on'
+ * in ddr4 case the terminations are rttWR and rttPARK and the odt must be always 'on' 0x1498 = 0xf
+ */
+u32 odt_config = 1;
+
+u32 nominal_avs;
+u32 extension_avs;
+
+u32 is_pll_before_init = 0, is_adll_calib_before_init = 1, is_dfs_in_init = 0;
+u32 dfs_low_freq;
+
+u32 g_rtt_nom_cs0, g_rtt_nom_cs1;
+u8 calibration_update_control;	/* 2 external only, 1 is internal only */
+
+enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM];
+enum auto_tune_stage training_stage = INIT_CONTROLLER;
+u32 finger_test = 0, p_finger_start = 11, p_finger_end = 64,
+	n_finger_start = 11, n_finger_end = 64,
+	p_finger_step = 3, n_finger_step = 3;
+u32 clamp_tbl[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
+
+/* Initiate to 0xff, this variable is define by user in debug mode */
+u32 mode_2t = 0xff;
+u32 xsb_validate_type = 0;
+u32 xsb_validation_base_address = 0xf000;
+u32 first_active_if = 0;
+u32 dfs_low_phy1 = 0x1f;
+u32 multicast_id = 0;
+int use_broadcast = 0;
+struct hws_tip_freq_config_info *freq_info_table = NULL;
+u8 is_cbe_required = 0;
+u32 debug_mode = 0;
+u32 delay_enable = 0;
+int rl_mid_freq_wa = 0;
+
+u32 effective_cs = 0;
+
+u32 vref_init_val = 0x4;
+u32 ck_delay = PARAM_UNDEFINED;
+
+/* Design guidelines parameters */
+u32 g_zpri_data = PARAM_UNDEFINED; /* controller data - P drive strength */
+u32 g_znri_data = PARAM_UNDEFINED; /* controller data - N drive strength */
+u32 g_zpri_ctrl = PARAM_UNDEFINED; /* controller C/A - P drive strength */
+u32 g_znri_ctrl = PARAM_UNDEFINED; /* controller C/A - N drive strength */
+
+u32 g_zpodt_data = PARAM_UNDEFINED; /* controller data - P ODT */
+u32 g_znodt_data = PARAM_UNDEFINED; /* controller data - N ODT */
+u32 g_zpodt_ctrl = PARAM_UNDEFINED; /* controller data - P ODT */
+u32 g_znodt_ctrl = PARAM_UNDEFINED; /* controller data - N ODT */
+
+u32 g_odt_config = PARAM_UNDEFINED;
+u32 g_rtt_nom = PARAM_UNDEFINED;
+u32 g_rtt_wr = PARAM_UNDEFINED;
+u32 g_dic = PARAM_UNDEFINED;
+u32 g_rtt_park = PARAM_UNDEFINED;
+
+u32 mask_tune_func = (SET_MEDIUM_FREQ_MASK_BIT |
+		      WRITE_LEVELING_MASK_BIT |
+		      LOAD_PATTERN_2_MASK_BIT |
+		      READ_LEVELING_MASK_BIT |
+		      SET_TARGET_FREQ_MASK_BIT |
+		      WRITE_LEVELING_TF_MASK_BIT |
+		      READ_LEVELING_TF_MASK_BIT |
+		      CENTRALIZATION_RX_MASK_BIT |
+		      CENTRALIZATION_TX_MASK_BIT);
+
+static int ddr3_tip_ddr3_training_main_flow(u32 dev_num);
+static int ddr3_tip_write_odt(u32 dev_num, enum hws_access_type access_type,
+			      u32 if_id, u32 cl_value, u32 cwl_value);
+static int ddr3_tip_ddr3_auto_tune(u32 dev_num);
+
+#ifdef ODT_TEST_SUPPORT
+static int odt_test(u32 dev_num, enum hws_algo_type algo_type);
+#endif
+
+int adll_calibration(u32 dev_num, enum hws_access_type access_type,
+		     u32 if_id, enum mv_ddr_freq frequency);
+static int ddr3_tip_set_timing(u32 dev_num, enum hws_access_type access_type,
+			       u32 if_id, enum mv_ddr_freq frequency);
+
+static u8 mem_size_config[MV_DDR_DIE_CAP_LAST] = {
+	0x2,			/* 512Mbit  */
+	0x3,			/* 1Gbit    */
+	0x0,			/* 2Gbit    */
+	0x4,			/* 4Gbit    */
+	0x5,			/* 8Gbit    */
+	0x0, /* TODO: placeholder for 16-Mbit die capacity */
+	0x0, /* TODO: placeholder for 32-Mbit die capacity */
+	0x0, /* TODO: placeholder for 12-Mbit die capacity */
+	0x0  /* TODO: placeholder for 24-Mbit die capacity */
+};
+
+static u8 cs_mask2_num[] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
+
+static struct reg_data odpg_default_value[] = {
+	{0x1034, 0x38000, MASK_ALL_BITS},
+	{0x1038, 0x0, MASK_ALL_BITS},
+	{0x10b0, 0x0, MASK_ALL_BITS},
+	{0x10b8, 0x0, MASK_ALL_BITS},
+	{0x10c0, 0x0, MASK_ALL_BITS},
+	{0x10f0, 0x0, MASK_ALL_BITS},
+	{0x10f4, 0x0, MASK_ALL_BITS},
+	{0x10f8, 0xff, MASK_ALL_BITS},
+	{0x10fc, 0xffff, MASK_ALL_BITS},
+	{0x1130, 0x0, MASK_ALL_BITS},
+	{0x1830, 0x2000000, MASK_ALL_BITS},
+	{0x14d0, 0x0, MASK_ALL_BITS},
+	{0x14d4, 0x0, MASK_ALL_BITS},
+	{0x14d8, 0x0, MASK_ALL_BITS},
+	{0x14dc, 0x0, MASK_ALL_BITS},
+	{0x1454, 0x0, MASK_ALL_BITS},
+	{0x1594, 0x0, MASK_ALL_BITS},
+	{0x1598, 0x0, MASK_ALL_BITS},
+	{0x159c, 0x0, MASK_ALL_BITS},
+	{0x15a0, 0x0, MASK_ALL_BITS},
+	{0x15a4, 0x0, MASK_ALL_BITS},
+	{0x15a8, 0x0, MASK_ALL_BITS},
+	{0x15ac, 0x0, MASK_ALL_BITS},
+	{0x1604, 0x0, MASK_ALL_BITS},
+	{0x1608, 0x0, MASK_ALL_BITS},
+	{0x160c, 0x0, MASK_ALL_BITS},
+	{0x1610, 0x0, MASK_ALL_BITS},
+	{0x1614, 0x0, MASK_ALL_BITS},
+	{0x1618, 0x0, MASK_ALL_BITS},
+	{0x1624, 0x0, MASK_ALL_BITS},
+	{0x1690, 0x0, MASK_ALL_BITS},
+	{0x1694, 0x0, MASK_ALL_BITS},
+	{0x1698, 0x0, MASK_ALL_BITS},
+	{0x169c, 0x0, MASK_ALL_BITS},
+	{0x14b8, 0x6f67, MASK_ALL_BITS},
+	{0x1630, 0x0, MASK_ALL_BITS},
+	{0x1634, 0x0, MASK_ALL_BITS},
+	{0x1638, 0x0, MASK_ALL_BITS},
+	{0x163c, 0x0, MASK_ALL_BITS},
+	{0x16b0, 0x0, MASK_ALL_BITS},
+	{0x16b4, 0x0, MASK_ALL_BITS},
+	{0x16b8, 0x0, MASK_ALL_BITS},
+	{0x16bc, 0x0, MASK_ALL_BITS},
+	{0x16c0, 0x0, MASK_ALL_BITS},
+	{0x16c4, 0x0, MASK_ALL_BITS},
+	{0x16c8, 0x0, MASK_ALL_BITS},
+	{0x16cc, 0x1, MASK_ALL_BITS},
+	{0x16f0, 0x1, MASK_ALL_BITS},
+	{0x16f4, 0x0, MASK_ALL_BITS},
+	{0x16f8, 0x0, MASK_ALL_BITS},
+	{0x16fc, 0x0, MASK_ALL_BITS}
+};
+
+/* MR cmd and addr definitions */
+struct mv_ddr_mr_data mr_data[] = {
+	{MRS0_CMD, MR0_REG},
+	{MRS1_CMD, MR1_REG},
+	{MRS2_CMD, MR2_REG},
+	{MRS3_CMD, MR3_REG}
+};
+
+/* inverse pads */
+static int ddr3_tip_pad_inv(void)
+{
+	u32 sphy, data;
+	u32 sphy_max = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+	u32 ck_swap_ctrl_sphy;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (sphy = 0; sphy < sphy_max; sphy++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sphy);
+		if (tm->interface_params[0].
+		    as_bus_params[sphy].is_dqs_swap == 1) {
+			data = (INVERT_PAD << INV_PAD4_OFFS |
+				INVERT_PAD << INV_PAD5_OFFS);
+			/* dqs swap */
+			ddr3_tip_bus_read_modify_write(0, ACCESS_TYPE_UNICAST,
+						       0, sphy,
+						       DDR_PHY_DATA,
+						       PHY_CTRL_PHY_REG,
+						       data, data);
+		}
+
+		if (tm->interface_params[0].as_bus_params[sphy].
+		    is_ck_swap == 1 && sphy == 0) {
+/* TODO: move this code to per platform one */
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X)
+			/* clock swap for both cs0 and cs1 */
+			data = (INVERT_PAD << INV_PAD2_OFFS |
+				INVERT_PAD << INV_PAD6_OFFS |
+				INVERT_PAD << INV_PAD4_OFFS |
+				INVERT_PAD << INV_PAD5_OFFS);
+			ck_swap_ctrl_sphy = CK_SWAP_CTRL_PHY_NUM;
+			ddr3_tip_bus_read_modify_write(0, ACCESS_TYPE_UNICAST,
+						       0, ck_swap_ctrl_sphy,
+						       DDR_PHY_CONTROL,
+						       PHY_CTRL_PHY_REG,
+						       data, data);
+#else /* !CONFIG_ARMADA_38X && !CONFIG_ARMADA_39X && !A70X0 && !A80X0 && !A3900 */
+#pragma message "unknown platform to configure ddr clock swap"
+#endif
+		}
+	}
+
+	return MV_OK;
+}
+
+static int ddr3_tip_rank_control(u32 dev_num, u32 if_id);
+
+/*
+ * Update global training parameters by data from user
+ */
+int ddr3_tip_tune_training_params(u32 dev_num,
+				  struct tune_train_params *params)
+{
+	if (params->ck_delay != PARAM_UNDEFINED)
+		ck_delay = params->ck_delay;
+	if (params->phy_reg3_val != PARAM_UNDEFINED)
+		phy_reg3_val = params->phy_reg3_val;
+	if (params->g_rtt_nom != PARAM_UNDEFINED)
+		g_rtt_nom = params->g_rtt_nom;
+	if (params->g_rtt_wr != PARAM_UNDEFINED)
+		g_rtt_wr = params->g_rtt_wr;
+	if (params->g_dic != PARAM_UNDEFINED)
+		g_dic = params->g_dic;
+	if (params->g_odt_config != PARAM_UNDEFINED)
+		g_odt_config = params->g_odt_config;
+	if (params->g_zpri_data != PARAM_UNDEFINED)
+		g_zpri_data = params->g_zpri_data;
+	if (params->g_znri_data != PARAM_UNDEFINED)
+		g_znri_data = params->g_znri_data;
+	if (params->g_zpri_ctrl != PARAM_UNDEFINED)
+		g_zpri_ctrl = params->g_zpri_ctrl;
+	if (params->g_znri_ctrl != PARAM_UNDEFINED)
+		g_znri_ctrl = params->g_znri_ctrl;
+	if (params->g_zpodt_data != PARAM_UNDEFINED)
+		g_zpodt_data = params->g_zpodt_data;
+	if (params->g_znodt_data != PARAM_UNDEFINED)
+		g_znodt_data = params->g_znodt_data;
+	if (params->g_zpodt_ctrl != PARAM_UNDEFINED)
+		g_zpodt_ctrl = params->g_zpodt_ctrl;
+	if (params->g_znodt_ctrl != PARAM_UNDEFINED)
+		g_znodt_ctrl = params->g_znodt_ctrl;
+	if (params->g_rtt_park != PARAM_UNDEFINED)
+		g_rtt_park = params->g_rtt_park;
+
+
+	DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+			  ("DGL parameters: 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
+			   g_zpri_data, g_znri_data, g_zpri_ctrl, g_znri_ctrl, g_zpodt_data, g_znodt_data,
+			   g_zpodt_ctrl, g_znodt_ctrl, g_rtt_nom, g_dic, g_odt_config, g_rtt_wr));
+
+	return MV_OK;
+}
+
+/*
+ * Configure CS
+ */
+int ddr3_tip_configure_cs(u32 dev_num, u32 if_id, u32 cs_num, u32 enable)
+{
+	u32 data, addr_hi, data_high;
+	u32 mem_index;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (enable == 1) {
+		data = (tm->interface_params[if_id].bus_width ==
+			MV_DDR_DEV_WIDTH_8BIT) ? 0 : 1;
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      SDRAM_ADDR_CTRL_REG, (data << (cs_num * 4)),
+			      0x3 << (cs_num * 4)));
+		mem_index = tm->interface_params[if_id].memory_size;
+
+		addr_hi = mem_size_config[mem_index] & 0x3;
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      SDRAM_ADDR_CTRL_REG,
+			      (addr_hi << (2 + cs_num * 4)),
+			      0x3 << (2 + cs_num * 4)));
+
+		data_high = (mem_size_config[mem_index] & 0x4) >> 2;
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      SDRAM_ADDR_CTRL_REG,
+			      data_high << (20 + cs_num), 1 << (20 + cs_num)));
+
+		/* Enable Address Select Mode */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      SDRAM_ADDR_CTRL_REG, 1 << (16 + cs_num),
+			      1 << (16 + cs_num)));
+	}
+	switch (cs_num) {
+	case 0:
+	case 1:
+	case 2:
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUNIT_CTRL_LOW_REG, (enable << (cs_num + 11)),
+			      1 << (cs_num + 11)));
+		break;
+	case 3:
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUNIT_CTRL_LOW_REG, (enable << 15), 1 << 15));
+		break;
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Init Controller Flow
+ */
+int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_prm)
+{
+	u32 if_id;
+	u32 cs_num;
+	u32 t_ckclk = 0, t_wr = 0, t2t = 0;
+	u32 data_value = 0, cs_cnt = 0,
+		mem_mask = 0, bus_index = 0;
+	enum mv_ddr_speed_bin speed_bin_index = SPEED_BIN_DDR_2133N;
+	u32 cs_mask = 0;
+	u32 cl_value = 0, cwl_val = 0;
+	u32 bus_cnt = 0, adll_tap = 0;
+	enum hws_access_type access_type = ACCESS_TYPE_UNICAST;
+	u32 data_read[MAX_INTERFACE_NUM];
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	enum mv_ddr_timing timing;
+	enum mv_ddr_freq freq = tm->interface_params[0].memory_freq;
+
+	DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+			  ("Init_controller, do_mrs_phy=%d, is_ctrl64_bit=%d\n",
+			   init_cntr_prm->do_mrs_phy,
+			   init_cntr_prm->is_ctrl64_bit));
+
+	if (init_cntr_prm->init_phy == 1) {
+		CHECK_STATUS(ddr3_tip_configure_phy(dev_num));
+	}
+
+	if (generic_init_controller == 1) {
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+					  ("active IF %d\n", if_id));
+			mem_mask = 0;
+			for (bus_index = 0;
+			     bus_index < octets_per_if_num;
+			     bus_index++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_index);
+				mem_mask |=
+					tm->interface_params[if_id].
+					as_bus_params[bus_index].mirror_enable_bitmask;
+			}
+
+			if (mem_mask != 0) {
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, ACCESS_TYPE_MULTICAST,
+					      if_id, DUAL_DUNIT_CFG_REG, 0,
+					      0x8));
+			}
+
+			speed_bin_index =
+				tm->interface_params[if_id].
+				speed_bin_index;
+
+			/* t_ckclk is external clock */
+			t_ckclk = (MEGA / mv_ddr_freq_get(freq));
+
+			if (MV_DDR_IS_HALF_BUS_DRAM_MODE(tm->bus_act_mask, octets_per_if_num))
+				data_value = (0x4000 | 0 | 0x1000000) & ~(1 << 26);
+			else
+				data_value = (0x4000 | 0x8000 | 0x1000000) & ~(1 << 26);
+
+			/* Interface Bus Width */
+			/* SRMode */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      SDRAM_CFG_REG, data_value,
+				      0x100c000));
+
+			/* Interleave first command pre-charge enable (TBD) */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      SDRAM_OPEN_PAGES_CTRL_REG, (1 << 10),
+				      (1 << 10)));
+
+			/* Reset divider_b assert -> de-assert */
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+						       SDRAM_CFG_REG,
+						       0x0 << PUP_RST_DIVIDER_OFFS,
+						       PUP_RST_DIVIDER_MASK << PUP_RST_DIVIDER_OFFS));
+
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+						       SDRAM_CFG_REG,
+						       0x1 << PUP_RST_DIVIDER_OFFS,
+						       PUP_RST_DIVIDER_MASK << PUP_RST_DIVIDER_OFFS));
+
+			/* PHY configuration */
+			/*
+			 * Postamble Length = 1.5cc, Addresscntl to clk skew
+			 * \BD, Preamble length normal, parralal ADLL enable
+			 */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DRAM_PHY_CFG_REG, 0x28, 0x3e));
+			if (init_cntr_prm->is_ctrl64_bit) {
+				/* positive edge */
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      DRAM_PHY_CFG_REG, 0x0,
+					      0xff80));
+			}
+
+			/* calibration block disable */
+			/* Xbar Read buffer select (for Internal access) */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      MAIN_PADS_CAL_MACH_CTRL_REG, 0x1200c,
+				      0x7dffe01c));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      MAIN_PADS_CAL_MACH_CTRL_REG,
+				      calibration_update_control << 3, 0x3 << 3));
+
+			/* Pad calibration control - enable */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      MAIN_PADS_CAL_MACH_CTRL_REG, 0x1, 0x1));
+			if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) < MV_TIP_REV_3) {
+				/* DDR3 rank ctrl \96 part of the generic code */
+				/* CS1 mirroring enable + w/a for JIRA DUNIT-14581 */
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      DDR3_RANK_CTRL_REG, 0x27, MASK_ALL_BITS));
+			}
+
+			cs_mask = 0;
+			data_value = 0x7;
+			/*
+			 * Address ctrl \96 Part of the Generic code
+			 * The next configuration is done:
+			 * 1)  Memory Size
+			 * 2) Bus_width
+			 * 3) CS#
+			 * 4) Page Number
+			 * Per Dunit get from the Map_topology the parameters:
+			 * Bus_width
+			 */
+
+			data_value =
+				(tm->interface_params[if_id].
+				 bus_width == MV_DDR_DEV_WIDTH_8BIT) ? 0 : 1;
+
+			/* create merge cs mask for all cs available in dunit */
+			for (bus_cnt = 0;
+			     bus_cnt < octets_per_if_num;
+			     bus_cnt++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+				cs_mask |=
+					tm->interface_params[if_id].
+					as_bus_params[bus_cnt].cs_bitmask;
+			}
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+					  ("Init_controller IF %d cs_mask %d\n",
+					   if_id, cs_mask));
+			/*
+			 * Configure the next upon the Map Topology \96 If the
+			 * Dunit is CS0 Configure CS0 if it is multi CS
+			 * configure them both:  The Bust_width it\92s the
+			 * Memory Bus width \96 x8 or x16
+			 */
+			for (cs_cnt = 0; cs_cnt < MAX_CS_NUM; cs_cnt++) {
+				ddr3_tip_configure_cs(dev_num, if_id, cs_cnt,
+						      ((cs_mask & (1 << cs_cnt)) ? 1
+						       : 0));
+			}
+
+			if (init_cntr_prm->do_mrs_phy) {
+				/*
+				 * MR0 \96 Part of the Generic code
+				 * The next configuration is done:
+				 * 1) Burst Length
+				 * 2) CAS Latency
+				 * get for each dunit what is it Speed_bin &
+				 * Target Frequency. From those both parameters
+				 * get the appropriate Cas_l from the CL table
+				 */
+				cl_value =
+					tm->interface_params[if_id].
+					cas_l;
+				cwl_val =
+					tm->interface_params[if_id].
+					cas_wl;
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+						  ("cl_value 0x%x cwl_val 0x%x\n",
+						   cl_value, cwl_val));
+
+				t_wr = time_to_nclk(mv_ddr_speed_bin_timing_get
+							   (speed_bin_index,
+							    SPEED_BIN_TWR), t_ckclk);
+
+				data_value =
+					((cl_mask_table[cl_value] & 0x1) << 2) |
+					((cl_mask_table[cl_value] & 0xe) << 3);
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      MR0_REG, data_value,
+					      (0x7 << 4) | (1 << 2)));
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      MR0_REG, twr_mask_table[t_wr] << 9,
+					      0x7 << 9));
+
+				/*
+				 * MR1: Set RTT and DIC Design GL values
+				 * configured by user
+				 */
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, ACCESS_TYPE_MULTICAST,
+					      PARAM_NOT_CARE, MR1_REG,
+					      g_dic | g_rtt_nom, 0x266));
+
+				/* MR2 - Part of the Generic code */
+				/*
+				 * The next configuration is done:
+				 * 1)  SRT
+				 * 2) CAS Write Latency
+				 */
+				data_value = (cwl_mask_table[cwl_val] << 3);
+				data_value |=
+					((tm->interface_params[if_id].
+					  interface_temp ==
+					  MV_DDR_TEMP_HIGH) ? (1 << 7) : 0);
+				data_value |= g_rtt_wr;
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      MR2_REG, data_value,
+					      (0x7 << 3) | (0x1 << 7) | (0x3 <<
+									 9)));
+			}
+
+			ddr3_tip_write_odt(dev_num, access_type, if_id,
+					   cl_value, cwl_val);
+			ddr3_tip_set_timing(dev_num, access_type, if_id, freq);
+
+			if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) < MV_TIP_REV_3) {
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      DUNIT_CTRL_HIGH_REG, 0x1000119,
+					      0x100017F));
+			} else {
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      DUNIT_CTRL_HIGH_REG, 0x600177 |
+					      (init_cntr_prm->is_ctrl64_bit ?
+					      CPU_INTERJECTION_ENA_SPLIT_ENA << CPU_INTERJECTION_ENA_OFFS :
+					      CPU_INTERJECTION_ENA_SPLIT_DIS << CPU_INTERJECTION_ENA_OFFS),
+					      0x1600177 | CPU_INTERJECTION_ENA_MASK <<
+					      CPU_INTERJECTION_ENA_OFFS));
+			}
+
+			/* reset bit 7 */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DUNIT_CTRL_HIGH_REG,
+				      (init_cntr_prm->msys_init << 7), (1 << 7)));
+
+			timing = tm->interface_params[if_id].timing;
+
+			if (mode_2t != 0xff) {
+				t2t = mode_2t;
+			} else if (timing != MV_DDR_TIM_DEFAULT) {
+				t2t = (timing == MV_DDR_TIM_2T) ? 1 : 0;
+			} else {
+				/* calculate number of CS (per interface) */
+				cs_num = mv_ddr_cs_num_get();
+				t2t = (cs_num == 1) ? 0 : 1;
+			}
+
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DUNIT_CTRL_LOW_REG, t2t << 3,
+				      0x3 << 3));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DDR_TIMING_REG, 0x28 << 9, 0x3f << 9));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DDR_TIMING_REG, 0xa << 21, 0xff << 21));
+
+			/* move the block to ddr3_tip_set_timing - end */
+			/* AUTO_ZQC_TIMING */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      ZQC_CFG_REG, (AUTO_ZQC_TIMING | (2 << 20)),
+				      0x3fffff));
+			CHECK_STATUS(ddr3_tip_if_read
+				     (dev_num, access_type, if_id,
+				      DRAM_PHY_CFG_REG, data_read, 0x30));
+			data_value =
+				(data_read[if_id] == 0) ? (1 << 11) : 0;
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DUNIT_CTRL_HIGH_REG, data_value,
+				      (1 << 11)));
+
+			/* Set Active control for ODT write transactions */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_MULTICAST,
+				      PARAM_NOT_CARE, 0x1494, g_odt_config,
+				      MASK_ALL_BITS));
+
+			if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) == MV_TIP_REV_3) {
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      0x14a8, 0x900, 0x900));
+				/* wa: controls control sub-phy outputs floating during self-refresh */
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      0x16d0, 0, 0x8000));
+			}
+		}
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		CHECK_STATUS(ddr3_tip_rank_control(dev_num, if_id));
+
+		if (init_cntr_prm->do_mrs_phy)
+			ddr3_tip_pad_inv();
+
+		/* Pad calibration control - disable */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      MAIN_PADS_CAL_MACH_CTRL_REG, 0x0, 0x1));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      MAIN_PADS_CAL_MACH_CTRL_REG,
+			      calibration_update_control << 3, 0x3 << 3));
+	}
+
+
+	if (delay_enable != 0) {
+		adll_tap = MEGA / (mv_ddr_freq_get(freq) * 64);
+		ddr3_tip_cmd_addr_init_delay(dev_num, adll_tap);
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Rank Control Flow
+ */
+static int ddr3_tip_rev2_rank_control(u32 dev_num, u32 if_id)
+{
+	u32 data_value = 0,  bus_cnt = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (bus_cnt = 0; bus_cnt < octets_per_if_num; bus_cnt++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+		data_value |= tm->interface_params[if_id].as_bus_params[bus_cnt].
+			      cs_bitmask;
+
+		if (tm->interface_params[if_id].as_bus_params[bus_cnt].
+		    mirror_enable_bitmask == 1) {
+			/*
+			 * Check mirror_enable_bitmask
+			 * If it is enabled, CS + 4 bit in a word to be '1'
+			 */
+			if ((tm->interface_params[if_id].as_bus_params[bus_cnt].
+			     cs_bitmask & 0x1) != 0) {
+				data_value |= tm->interface_params[if_id].
+					      as_bus_params[bus_cnt].
+					      mirror_enable_bitmask << 4;
+			}
+
+			if ((tm->interface_params[if_id].as_bus_params[bus_cnt].
+			     cs_bitmask & 0x2) != 0) {
+				data_value |= tm->interface_params[if_id].
+					      as_bus_params[bus_cnt].
+					      mirror_enable_bitmask << 5;
+			}
+
+			if ((tm->interface_params[if_id].as_bus_params[bus_cnt].
+			     cs_bitmask & 0x4) != 0) {
+				data_value |= tm->interface_params[if_id].
+					      as_bus_params[bus_cnt].
+					      mirror_enable_bitmask << 6;
+			}
+
+			if ((tm->interface_params[if_id].as_bus_params[bus_cnt].
+			     cs_bitmask & 0x8) != 0) {
+				data_value |= tm->interface_params[if_id].
+					      as_bus_params[bus_cnt].
+					      mirror_enable_bitmask << 7;
+			}
+		}
+	}
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_UNICAST, if_id, DDR3_RANK_CTRL_REG,
+		      data_value, 0xff));
+
+	return MV_OK;
+}
+
+static int ddr3_tip_rev3_rank_control(u32 dev_num, u32 if_id)
+{
+	u32 data_value = 0, bus_cnt;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (bus_cnt = 1; bus_cnt < octets_per_if_num; bus_cnt++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+		if ((tm->interface_params[if_id].
+		     as_bus_params[0].cs_bitmask !=
+		     tm->interface_params[if_id].
+		     as_bus_params[bus_cnt].cs_bitmask) ||
+		    (tm->interface_params[if_id].
+		     as_bus_params[0].mirror_enable_bitmask !=
+		     tm->interface_params[if_id].
+		     as_bus_params[bus_cnt].mirror_enable_bitmask))
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("WARNING:Wrong configuration for pup #%d CS mask and CS mirroring for all pups should be the same\n",
+					   bus_cnt));
+	}
+
+	data_value |= tm->interface_params[if_id].
+		as_bus_params[0].cs_bitmask;
+	data_value |= tm->interface_params[if_id].
+		as_bus_params[0].mirror_enable_bitmask << 4;
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_UNICAST, if_id, DDR3_RANK_CTRL_REG,
+		      data_value, 0xff));
+
+	return MV_OK;
+}
+
+static int ddr3_tip_rank_control(u32 dev_num, u32 if_id)
+{
+	if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) == MV_TIP_REV_2)
+		return ddr3_tip_rev2_rank_control(dev_num, if_id);
+	else
+		return ddr3_tip_rev3_rank_control(dev_num, if_id);
+}
+
+/*
+ * Algorithm Parameters Validation
+ */
+int ddr3_tip_validate_algo_var(u32 value, u32 fail_value, char *var_name)
+{
+	if (value == fail_value) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("Error: %s is not initialized (Algo Components Validation)\n",
+				   var_name));
+		return 0;
+	}
+
+	return 1;
+}
+
+int ddr3_tip_validate_algo_ptr(void *ptr, void *fail_value, char *ptr_name)
+{
+	if (ptr == fail_value) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("Error: %s is not initialized (Algo Components Validation)\n",
+				   ptr_name));
+		return 0;
+	}
+
+	return 1;
+}
+
+int ddr3_tip_validate_algo_components(u8 dev_num)
+{
+	int status = 1;
+
+	/* Check DGL parameters*/
+	status &= ddr3_tip_validate_algo_var(ck_delay, PARAM_UNDEFINED, "ck_delay");
+	status &= ddr3_tip_validate_algo_var(phy_reg3_val, PARAM_UNDEFINED, "phy_reg3_val");
+	status &= ddr3_tip_validate_algo_var(g_rtt_nom, PARAM_UNDEFINED, "g_rtt_nom");
+	status &= ddr3_tip_validate_algo_var(g_dic, PARAM_UNDEFINED, "g_dic");
+	status &= ddr3_tip_validate_algo_var(odt_config, PARAM_UNDEFINED, "odt_config");
+	status &= ddr3_tip_validate_algo_var(g_zpri_data, PARAM_UNDEFINED, "g_zpri_data");
+	status &= ddr3_tip_validate_algo_var(g_znri_data, PARAM_UNDEFINED, "g_znri_data");
+	status &= ddr3_tip_validate_algo_var(g_zpri_ctrl, PARAM_UNDEFINED, "g_zpri_ctrl");
+	status &= ddr3_tip_validate_algo_var(g_znri_ctrl, PARAM_UNDEFINED, "g_znri_ctrl");
+	status &= ddr3_tip_validate_algo_var(g_zpodt_data, PARAM_UNDEFINED, "g_zpodt_data");
+	status &= ddr3_tip_validate_algo_var(g_znodt_data, PARAM_UNDEFINED, "g_znodt_data");
+	status &= ddr3_tip_validate_algo_var(g_zpodt_ctrl, PARAM_UNDEFINED, "g_zpodt_ctrl");
+	status &= ddr3_tip_validate_algo_var(g_znodt_ctrl, PARAM_UNDEFINED, "g_znodt_ctrl");
+
+	/* Check functions pointers */
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].tip_dunit_mux_select_func,
+					     NULL, "tip_dunit_mux_select_func");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].mv_ddr_dunit_write,
+					     NULL, "mv_ddr_dunit_write");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].mv_ddr_dunit_read,
+					     NULL, "mv_ddr_dunit_read");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].mv_ddr_phy_write,
+					     NULL, "mv_ddr_phy_write");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].mv_ddr_phy_read,
+					     NULL, "mv_ddr_phy_read");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].tip_get_freq_config_info_func,
+					     NULL, "tip_get_freq_config_info_func");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].tip_set_freq_divider_func,
+					     NULL, "tip_set_freq_divider_func");
+	status &= ddr3_tip_validate_algo_ptr(config_func_info[dev_num].tip_get_clock_ratio,
+					     NULL, "tip_get_clock_ratio");
+
+	status &= ddr3_tip_validate_algo_ptr(dq_map_table, NULL, "dq_map_table");
+	status &= ddr3_tip_validate_algo_var(dfs_low_freq, 0, "dfs_low_freq");
+
+	return (status == 1) ? MV_OK : MV_NOT_INITIALIZED;
+}
+
+
+int ddr3_pre_algo_config(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* Set Bus3 ECC training mode */
+	if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) {
+		/* Set Bus3 ECC MUX */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
+			      DRAM_PINS_MUX_REG, 0x100, 0x100));
+	}
+
+	/* Set regular ECC training mode (bus4 and bus 3) */
+	if ((DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask)) ||
+	    (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) ||
+	    (DDR3_IS_ECC_PUP8_MODE(tm->bus_act_mask))) {
+		/* Enable ECC Write MUX */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x100, 0x100));
+		/* General ECC enable */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
+			      SDRAM_CFG_REG, 0x40000, 0x40000));
+		/* Disable Read Data ECC MUX */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x0, 0x2));
+	}
+
+	return MV_OK;
+}
+
+int ddr3_post_algo_config(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	int status;
+
+	status = ddr3_post_run_alg();
+	if (MV_OK != status) {
+		printf("DDR3 Post Run Alg - FAILED 0x%x\n", status);
+		return status;
+	}
+
+	/* Un_set ECC training mode */
+	if ((DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask)) ||
+	    (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) ||
+	    (DDR3_IS_ECC_PUP8_MODE(tm->bus_act_mask))) {
+		/* Disable ECC Write MUX */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x0, 0x100));
+		/* General ECC and Bus3 ECC MUX remains enabled */
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Run Training Flow
+ */
+int hws_ddr3_tip_run_alg(u32 dev_num, enum hws_algo_type algo_type)
+{
+	int status = MV_OK;
+
+	status = ddr3_pre_algo_config();
+	if (MV_OK != status) {
+		printf("DDR3 Pre Algo Config - FAILED 0x%x\n", status);
+		return status;
+	}
+
+#ifdef ODT_TEST_SUPPORT
+	if (finger_test == 1)
+		return odt_test(dev_num, algo_type);
+#endif
+
+	if (algo_type == ALGO_TYPE_DYNAMIC) {
+		status = ddr3_tip_ddr3_auto_tune(dev_num);
+	}
+
+	if (status != MV_OK) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("********   DRAM initialization Failed (res 0x%x)   ********\n",
+				   status));
+		return status;
+	}
+
+	status = ddr3_post_algo_config();
+	if (MV_OK != status) {
+		printf("DDR3 Post Algo Config - FAILED 0x%x\n", status);
+		return status;
+	}
+
+	return status;
+}
+
+#ifdef ODT_TEST_SUPPORT
+/*
+ * ODT Test
+ */
+static int odt_test(u32 dev_num, enum hws_algo_type algo_type)
+{
+	int ret = MV_OK, ret_tune = MV_OK;
+	int pfinger_val = 0, nfinger_val;
+
+	for (pfinger_val = p_finger_start; pfinger_val <= p_finger_end;
+	     pfinger_val += p_finger_step) {
+		for (nfinger_val = n_finger_start; nfinger_val <= n_finger_end;
+		     nfinger_val += n_finger_step) {
+			if (finger_test != 0) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+						  ("pfinger_val %d nfinger_val %d\n",
+						   pfinger_val, nfinger_val));
+				/*
+				 * TODO: need to check the correctness
+				 * of the following two lines.
+				 */
+				g_zpodt_data = pfinger_val;
+				g_znodt_data = nfinger_val;
+			}
+
+			if (algo_type == ALGO_TYPE_DYNAMIC) {
+				ret = ddr3_tip_ddr3_auto_tune(dev_num);
+			}
+		}
+	}
+
+	if (ret_tune != MV_OK) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("Run_alg: tuning failed %d\n", ret_tune));
+		ret = (ret == MV_OK) ? ret_tune : ret;
+	}
+
+	return ret;
+}
+#endif
+
+/*
+ * Select Controller
+ */
+int hws_ddr3_tip_select_ddr_controller(u32 dev_num, int enable)
+{
+	return config_func_info[dev_num].
+		tip_dunit_mux_select_func((u8)dev_num, enable);
+}
+
+/*
+ * Dunit Register Write
+ */
+int ddr3_tip_if_write(u32 dev_num, enum hws_access_type interface_access,
+		      u32 if_id, u32 reg_addr, u32 data_value, u32 mask)
+{
+	config_func_info[dev_num].mv_ddr_dunit_write(reg_addr, mask, data_value);
+
+	return MV_OK;
+}
+
+/*
+ * Dunit Register Read
+ */
+int ddr3_tip_if_read(u32 dev_num, enum hws_access_type interface_access,
+		     u32 if_id, u32 reg_addr, u32 *data, u32 mask)
+{
+	config_func_info[dev_num].mv_ddr_dunit_read(reg_addr, mask, data);
+
+	return MV_OK;
+}
+
+/*
+ * Dunit Register Polling
+ */
+int ddr3_tip_if_polling(u32 dev_num, enum hws_access_type access_type,
+			u32 if_id, u32 exp_value, u32 mask, u32 offset,
+			u32 poll_tries)
+{
+	u32 poll_cnt = 0, interface_num = 0, start_if, end_if;
+	u32 read_data[MAX_INTERFACE_NUM];
+	int ret;
+	int is_fail = 0, is_if_fail;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (access_type == ACCESS_TYPE_MULTICAST) {
+		start_if = 0;
+		end_if = MAX_INTERFACE_NUM - 1;
+	} else {
+		start_if = if_id;
+		end_if = if_id;
+	}
+
+	for (interface_num = start_if; interface_num <= end_if; interface_num++) {
+		/* polling bit 3 for n times */
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, interface_num);
+
+		is_if_fail = 0;
+		for (poll_cnt = 0; poll_cnt < poll_tries; poll_cnt++) {
+			ret =
+				ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST,
+						 interface_num, offset, read_data,
+						 mask);
+			if (ret != MV_OK)
+				return ret;
+
+			if (read_data[interface_num] == exp_value)
+				break;
+		}
+
+		if (poll_cnt >= poll_tries) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("max poll IF #%d\n", interface_num));
+			is_fail = 1;
+			is_if_fail = 1;
+		}
+
+		training_result[training_stage][interface_num] =
+			(is_if_fail == 1) ? TEST_FAILED : TEST_SUCCESS;
+	}
+
+	return (is_fail == 0) ? MV_OK : MV_FAIL;
+}
+
+/*
+ * Bus read access
+ */
+int ddr3_tip_bus_read(u32 dev_num, u32 if_id,
+		      enum hws_access_type phy_access, u32 phy_id,
+		      enum hws_ddr_phy phy_type, u32 reg_addr, u32 *data)
+{
+	return config_func_info[dev_num].
+		mv_ddr_phy_read(phy_access, phy_id, phy_type, reg_addr, data);
+}
+
+/*
+ * Bus write access
+ */
+int ddr3_tip_bus_write(u32 dev_num, enum hws_access_type interface_access,
+		       u32 if_id, enum hws_access_type phy_access,
+		       u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr,
+		       u32 data_value)
+{
+	return config_func_info[dev_num].
+		mv_ddr_phy_write(phy_access, phy_id, phy_type, reg_addr, data_value, OPERATION_WRITE);
+}
+
+
+/*
+ * Phy read-modify-write
+ */
+int ddr3_tip_bus_read_modify_write(u32 dev_num, enum hws_access_type access_type,
+				   u32 interface_id, u32 phy_id,
+				   enum hws_ddr_phy phy_type, u32 reg_addr,
+				   u32 data_value, u32 reg_mask)
+{
+	u32 data_val = 0, if_id, start_if, end_if;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (access_type == ACCESS_TYPE_MULTICAST) {
+		start_if = 0;
+		end_if = MAX_INTERFACE_NUM - 1;
+	} else {
+		start_if = interface_id;
+		end_if = interface_id;
+	}
+
+	for (if_id = start_if; if_id <= end_if; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		CHECK_STATUS(ddr3_tip_bus_read
+			     (dev_num, if_id, ACCESS_TYPE_UNICAST, phy_id,
+			      phy_type, reg_addr, &data_val));
+		data_value = (data_val & (~reg_mask)) | (data_value & reg_mask);
+		CHECK_STATUS(ddr3_tip_bus_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      ACCESS_TYPE_UNICAST, phy_id, phy_type, reg_addr,
+			      data_value));
+	}
+
+	return MV_OK;
+}
+
+/*
+ * ADLL Calibration
+ */
+int adll_calibration(u32 dev_num, enum hws_access_type access_type,
+		     u32 if_id, enum mv_ddr_freq frequency)
+{
+	struct hws_tip_freq_config_info freq_config_info;
+	u32 bus_cnt = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* Reset Diver_b assert -> de-assert */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id, SDRAM_CFG_REG,
+		      0, 0x10000000));
+	mdelay(10);
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id, SDRAM_CFG_REG,
+		      0x10000000, 0x10000000));
+
+	CHECK_STATUS(config_func_info[dev_num].
+		     tip_get_freq_config_info_func((u8)dev_num, frequency,
+						   &freq_config_info));
+
+	for (bus_cnt = 0; bus_cnt < octets_per_if_num; bus_cnt++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+		CHECK_STATUS(ddr3_tip_bus_read_modify_write
+			     (dev_num, access_type, if_id, bus_cnt,
+			      DDR_PHY_DATA, ADLL_CFG0_PHY_REG,
+			      freq_config_info.bw_per_freq << 8, 0x700));
+		CHECK_STATUS(ddr3_tip_bus_read_modify_write
+			     (dev_num, access_type, if_id, bus_cnt,
+			      DDR_PHY_DATA, ADLL_CFG2_PHY_REG,
+			      freq_config_info.rate_per_freq, 0x7));
+	}
+
+	for (bus_cnt = 0; bus_cnt < DDR_IF_CTRL_SUBPHYS_NUM; bus_cnt++) {
+		CHECK_STATUS(ddr3_tip_bus_read_modify_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id, bus_cnt,
+			      DDR_PHY_CONTROL, ADLL_CFG0_PHY_REG,
+			      freq_config_info.bw_per_freq << 8, 0x700));
+		CHECK_STATUS(ddr3_tip_bus_read_modify_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id, bus_cnt,
+			      DDR_PHY_CONTROL, ADLL_CFG2_PHY_REG,
+			      freq_config_info.rate_per_freq, 0x7));
+	}
+
+	/* DUnit to Phy drive post edge, ADLL reset assert de-assert */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id, DRAM_PHY_CFG_REG,
+		      0, (0x80000000 | 0x40000000)));
+	mdelay(100 / (mv_ddr_freq_get(frequency)) / mv_ddr_freq_get(MV_DDR_FREQ_LOW_FREQ));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id, DRAM_PHY_CFG_REG,
+		      (0x80000000 | 0x40000000), (0x80000000 | 0x40000000)));
+
+	/* polling for ADLL Done */
+	if (ddr3_tip_if_polling(dev_num, access_type, if_id,
+				0x3ff03ff, 0x3ff03ff, PHY_LOCK_STATUS_REG,
+				MAX_POLLING_ITERATIONS) != MV_OK) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("Freq_set: DDR3 poll failed(1)"));
+	}
+
+	/* pup data_pup reset assert-> deassert */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id, SDRAM_CFG_REG,
+		      0, 0x60000000));
+	mdelay(10);
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id, SDRAM_CFG_REG,
+		      0x60000000, 0x60000000));
+
+	return MV_OK;
+}
+
+int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type access_type,
+		      u32 if_id, enum mv_ddr_freq frequency)
+{
+	u32 cl_value = 0, cwl_value = 0, mem_mask = 0, val = 0,
+		bus_cnt = 0, t_wr = 0, t_ckclk = 0,
+		cnt_id;
+	u32 end_if, start_if;
+	u32 bus_index = 0;
+	int is_dll_off = 0;
+	enum mv_ddr_speed_bin speed_bin_index = 0;
+	struct hws_tip_freq_config_info freq_config_info;
+	enum hws_result *flow_result = training_result[training_stage];
+	u32 adll_tap = 0;
+	u32 cs_num;
+	u32 t2t;
+	u32 cs_mask[MAX_INTERFACE_NUM];
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int tclk;
+	enum mv_ddr_timing timing = tm->interface_params[if_id].timing;
+	u32 freq = mv_ddr_freq_get(frequency);
+
+	DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+			  ("dev %d access %d IF %d freq %d\n", dev_num,
+			   access_type, if_id, frequency));
+
+	if (frequency == MV_DDR_FREQ_LOW_FREQ)
+		is_dll_off = 1;
+	if (access_type == ACCESS_TYPE_MULTICAST) {
+		start_if = 0;
+		end_if = MAX_INTERFACE_NUM - 1;
+	} else {
+		start_if = if_id;
+		end_if = if_id;
+	}
+
+	/* calculate interface cs mask - Oferb 4/11 */
+	/* speed bin can be different for each interface */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		/* cs enable is active low */
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		cs_mask[if_id] = CS_BIT_MASK;
+		training_result[training_stage][if_id] = TEST_SUCCESS;
+		ddr3_tip_calc_cs_mask(dev_num, if_id, effective_cs,
+				      &cs_mask[if_id]);
+	}
+
+	/* speed bin can be different for each interface */
+	/*
+	 * moti b - need to remove the loop for multicas access functions
+	 * and loop the unicast access functions
+	 */
+	for (if_id = start_if; if_id <= end_if; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		flow_result[if_id] = TEST_SUCCESS;
+		speed_bin_index =
+			tm->interface_params[if_id].speed_bin_index;
+		if (tm->interface_params[if_id].memory_freq ==
+		    frequency) {
+			cl_value =
+				tm->interface_params[if_id].cas_l;
+			cwl_value =
+				tm->interface_params[if_id].cas_wl;
+		} else if (tm->cfg_src == MV_DDR_CFG_SPD) {
+			tclk = 1000000 / freq;
+			cl_value = mv_ddr_cl_calc(tm->timing_data[MV_DDR_TAA_MIN], tclk);
+			if (cl_value == 0) {
+				printf("mv_ddr: unsupported cas latency value found\n");
+				return MV_FAIL;
+			}
+			cwl_value = mv_ddr_cwl_calc(tclk);
+			if (cwl_value == 0) {
+				printf("mv_ddr: unsupported cas write latency value found\n");
+				return MV_FAIL;
+			}
+		} else {
+			cl_value = mv_ddr_cl_val_get(speed_bin_index, frequency);
+			cwl_value = mv_ddr_cwl_val_get(speed_bin_index, frequency);
+		}
+
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+				  ("Freq_set dev 0x%x access 0x%x if 0x%x freq 0x%x speed %d:\n\t",
+				   dev_num, access_type, if_id,
+				   frequency, speed_bin_index));
+
+		for (cnt_id = 0; cnt_id < MV_DDR_FREQ_LAST; cnt_id++) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE,
+					  ("%d ", mv_ddr_cl_val_get(speed_bin_index, cnt_id)));
+		}
+
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, ("\n"));
+		mem_mask = 0;
+		for (bus_index = 0; bus_index < octets_per_if_num;
+		     bus_index++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_index);
+			mem_mask |=
+				tm->interface_params[if_id].
+				as_bus_params[bus_index].mirror_enable_bitmask;
+		}
+
+		if (mem_mask != 0) {
+			/* motib redundent in KW28 */
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type,
+						       if_id,
+						       DUAL_DUNIT_CFG_REG, 0, 0x8));
+		}
+
+		/* dll state after exiting SR */
+		if (is_dll_off == 1) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DFS_REG, 0x1, 0x1));
+		} else {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      DFS_REG, 0, 0x1));
+		}
+
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      DUNIT_MMASK_REG, 0, 0x1));
+		/* DFS  - block  transactions */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      DFS_REG, 0x2, 0x2));
+
+		/* disable ODT in case of dll off */
+		if (is_dll_off == 1) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      0x1874, 0, 0x244));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      0x1884, 0, 0x244));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      0x1894, 0, 0x244));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      0x18a4, 0, 0x244));
+		}
+
+		/* DFS  - Enter Self-Refresh */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DFS_REG, 0x4,
+			      0x4));
+		/* polling on self refresh entry */
+		if (ddr3_tip_if_polling(dev_num, ACCESS_TYPE_UNICAST,
+					if_id, 0x8, 0x8, DFS_REG,
+					MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("Freq_set: DDR3 poll failed on SR entry\n"));
+		}
+
+		/* Calculate 2T mode */
+		if (mode_2t != 0xff) {
+			t2t = mode_2t;
+		} else if (timing != MV_DDR_TIM_DEFAULT) {
+			t2t = (timing == MV_DDR_TIM_2T) ? 1 : 0;
+		} else {
+			/* Calculate number of CS per interface */
+			cs_num = mv_ddr_cs_num_get();
+			t2t = (cs_num == 1) ? 0 : 1;
+		}
+
+
+		if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_INTERLEAVE_WA) == 1) {
+			/* Use 1T mode if 1:1 ratio configured */
+			if (config_func_info[dev_num].tip_get_clock_ratio(frequency) == 1) {
+				/* Low freq*/
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      SDRAM_OPEN_PAGES_CTRL_REG, 0x0, 0x3C0));
+				t2t = 0;
+			} else {
+				/* Middle or target freq */
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type, if_id,
+					      SDRAM_OPEN_PAGES_CTRL_REG, 0x3C0, 0x3C0));
+			}
+		}
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+					       DUNIT_CTRL_LOW_REG, t2t << 3, 0x3 << 3));
+
+		/* PLL configuration */
+		config_func_info[dev_num].tip_set_freq_divider_func(dev_num, if_id,
+								    frequency);
+
+		/* DFS  - CL/CWL/WR parameters after exiting SR */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DFS_REG,
+			      (cl_mask_table[cl_value] << 8), 0xf00));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DFS_REG,
+			      (cwl_mask_table[cwl_value] << 12), 0x7000));
+
+		t_ckclk = (MEGA / freq);
+		t_wr = time_to_nclk(mv_ddr_speed_bin_timing_get
+					   (speed_bin_index,
+					    SPEED_BIN_TWR), t_ckclk);
+
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DFS_REG,
+			      (twr_mask_table[t_wr] << 16), 0x70000));
+
+		/* Restore original RTT values if returning from DLL OFF mode */
+		if (is_dll_off == 1) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id, 0x1874,
+				      g_dic | g_rtt_nom, 0x266));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id, 0x1884,
+				      g_dic | g_rtt_nom, 0x266));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id, 0x1894,
+				      g_dic | g_rtt_nom, 0x266));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id, 0x18a4,
+				      g_dic | g_rtt_nom, 0x266));
+		}
+
+		/* Reset divider_b assert -> de-assert */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      SDRAM_CFG_REG, 0, 0x10000000));
+		mdelay(10);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      SDRAM_CFG_REG, 0x10000000, 0x10000000));
+
+		/* ADLL configuration function of process and frequency */
+		CHECK_STATUS(config_func_info[dev_num].
+			     tip_get_freq_config_info_func(dev_num, frequency,
+							   &freq_config_info));
+
+		/* TBD check milo5 using device ID ? */
+		for (bus_cnt = 0; bus_cnt < octets_per_if_num;
+		     bus_cnt++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+			CHECK_STATUS(ddr3_tip_bus_read_modify_write
+				     (dev_num, ACCESS_TYPE_UNICAST,
+				      if_id, bus_cnt, DDR_PHY_DATA,
+				      0x92,
+				      freq_config_info.
+				      bw_per_freq << 8
+				      /*freq_mask[dev_num][frequency] << 8 */
+				      , 0x700));
+			CHECK_STATUS(ddr3_tip_bus_read_modify_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      bus_cnt, DDR_PHY_DATA, 0x94,
+				      freq_config_info.rate_per_freq, 0x7));
+		}
+
+		/* Dunit to PHY drive post edge, ADLL reset assert -> de-assert */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      DRAM_PHY_CFG_REG, 0,
+			      (0x80000000 | 0x40000000)));
+		mdelay(100 / (freq / mv_ddr_freq_get(MV_DDR_FREQ_LOW_FREQ)));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      DRAM_PHY_CFG_REG, (0x80000000 | 0x40000000),
+			      (0x80000000 | 0x40000000)));
+
+		/* polling for ADLL Done */
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x3ff03ff,
+		     0x3ff03ff, PHY_LOCK_STATUS_REG,
+		     MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("Freq_set: DDR3 poll failed(1)\n"));
+		}
+
+		/* pup data_pup reset assert-> deassert */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      SDRAM_CFG_REG, 0, 0x60000000));
+		mdelay(10);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      SDRAM_CFG_REG, 0x60000000, 0x60000000));
+
+		/* Set proper timing params before existing Self-Refresh */
+		ddr3_tip_set_timing(dev_num, access_type, if_id, frequency);
+		if (delay_enable != 0) {
+			adll_tap = (is_dll_off == 1) ? 1000 : (MEGA / (freq * 64));
+			ddr3_tip_cmd_addr_init_delay(dev_num, adll_tap);
+		}
+
+		/* Exit SR */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DFS_REG, 0,
+			      0x4));
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x8, DFS_REG,
+		     MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("Freq_set: DDR3 poll failed(2)"));
+		}
+
+		/* Refresh Command */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      SDRAM_OP_REG, 0x2, 0xf1f));
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x1f,
+		     SDRAM_OP_REG, MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("Freq_set: DDR3 poll failed(3)"));
+		}
+
+		/* Release DFS Block */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DFS_REG, 0,
+			      0x2));
+		/* Controller to MBUS Retry - normal */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, DUNIT_MMASK_REG,
+			      0x1, 0x1));
+
+		/* MRO: Burst Length 8, CL , Auto_precharge 0x16cc */
+		val =
+			((cl_mask_table[cl_value] & 0x1) << 2) |
+			((cl_mask_table[cl_value] & 0xe) << 3);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id, MR0_REG,
+			      val, (0x7 << 4) | (1 << 2)));
+		/* MR2:  CWL = 10 , Auto Self-Refresh - disable */
+		val = (cwl_mask_table[cwl_value] << 3) | g_rtt_wr;
+		/*
+		 * nklein 24.10.13 - should not be here - leave value as set in
+		 * the init configuration val |= (1 << 9);
+		 * val |= ((tm->interface_params[if_id].
+		 * interface_temp == MV_DDR_TEMP_HIGH) ? (1 << 7) : 0);
+		 */
+		/* nklein 24.10.13 - see above comment */
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type,
+					       if_id, MR2_REG,
+					       val, (0x7 << 3) | (0x3 << 9)));
+
+		/* ODT TIMING */
+		val = ((cl_value - cwl_value + 1) << 4) |
+			((cl_value - cwl_value + 6) << 8) |
+			((cl_value - 1) << 12) | ((cl_value + 6) << 16);
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type,
+					       if_id, DDR_ODT_TIMING_LOW_REG,
+					       val, 0xffff0));
+		val = 0x91 | ((cwl_value - 1) << 8) | ((cwl_value + 5) << 12);
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type,
+					       if_id, DDR_ODT_TIMING_HIGH_REG,
+					       val, 0xffff));
+
+		/* in case of ddr4 need to set the receiver to odt always 'on' (odt_config = '0')
+		 * in case of ddr3 configure the odt through the timing
+		 */
+		if (odt_config != 0) {
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, DUNIT_ODT_CTRL_REG, 0xf, 0xf));
+		}
+		else {
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, DUNIT_ODT_CTRL_REG,
+						       0x30f, 0x30f));
+		}
+
+		/* re-write CL */
+		val = ((cl_mask_table[cl_value] & 0x1) << 2) |
+			((cl_mask_table[cl_value] & 0xe) << 3);
+
+		CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask, MR_CMD0,
+			val, (0x7 << 4) | (0x1 << 2)));
+
+		/* re-write CWL */
+		val = (cwl_mask_table[cwl_value] << 3) | g_rtt_wr;
+		CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask, MR_CMD2,
+			val, (0x7 << 3) | (0x3 << 9)));
+
+		if (mem_mask != 0) {
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type,
+						       if_id,
+						       DUAL_DUNIT_CFG_REG,
+						       1 << 3, 0x8));
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Set ODT values
+ */
+static int ddr3_tip_write_odt(u32 dev_num, enum hws_access_type access_type,
+			      u32 if_id, u32 cl_value, u32 cwl_value)
+{
+	/* ODT TIMING */
+	u32 val = (cl_value - cwl_value + 6);
+
+	val = ((cl_value - cwl_value + 1) << 4) | ((val & 0xf) << 8) |
+		(((cl_value - 1) & 0xf) << 12) |
+		(((cl_value + 6) & 0xf) << 16) | (((val & 0x10) >> 4) << 21);
+	val |= (((cl_value - 1) >> 4) << 22) | (((cl_value + 6) >> 4) << 23);
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       DDR_ODT_TIMING_LOW_REG, val, 0xffff0));
+	val = 0x91 | ((cwl_value - 1) << 8) | ((cwl_value + 5) << 12);
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       DDR_ODT_TIMING_HIGH_REG, val, 0xffff));
+	if (odt_additional == 1) {
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type,
+					       if_id,
+					       SDRAM_ODT_CTRL_HIGH_REG,
+					       0xf, 0xf));
+	}
+
+	/* ODT Active */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       DUNIT_ODT_CTRL_REG, 0xf, 0xf));
+
+	return MV_OK;
+}
+
+/*
+ * Set Timing values for training
+ */
+static int ddr3_tip_set_timing(u32 dev_num, enum hws_access_type access_type,
+			       u32 if_id, enum mv_ddr_freq frequency)
+{
+	u32 t_ckclk = 0, t_ras = 0;
+	u32 t_rcd = 0, t_rp = 0, t_wr = 0, t_wtr = 0, t_rrd = 0, t_rtp = 0,
+		t_rfc = 0, t_mod = 0, t_r2r = 0x3, t_r2r_high = 0,
+		t_r2w_w2r = 0x3, t_r2w_w2r_high = 0x1, t_w2w = 0x3;
+	u32 refresh_interval_cnt, t_hclk, t_refi, t_faw, t_pd, t_xpdll;
+	u32 val = 0, page_size = 0, mask = 0;
+	enum mv_ddr_speed_bin speed_bin_index;
+	enum mv_ddr_die_capacity memory_size = MV_DDR_DIE_CAP_2GBIT;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	u32 freq = mv_ddr_freq_get(frequency);
+
+	speed_bin_index = tm->interface_params[if_id].speed_bin_index;
+	memory_size = tm->interface_params[if_id].memory_size;
+	page_size = mv_ddr_page_size_get(tm->interface_params[if_id].bus_width, memory_size);
+	t_ckclk = (MEGA / freq);
+	/* HCLK in[ps] */
+	t_hclk = MEGA / (freq / config_func_info[dev_num].tip_get_clock_ratio(frequency));
+
+	t_refi = (tm->interface_params[if_id].interface_temp == MV_DDR_TEMP_HIGH) ? TREFI_HIGH : TREFI_LOW;
+	t_refi *= 1000;	/* psec */
+	refresh_interval_cnt = t_refi / t_hclk;	/* no units */
+
+	if (page_size == 1) {
+		t_faw = mv_ddr_speed_bin_timing_get(speed_bin_index, SPEED_BIN_TFAW1K);
+		t_faw = time_to_nclk(t_faw, t_ckclk);
+		t_faw = GET_MAX_VALUE(20, t_faw);
+	} else {	/* page size =2, we do not support page size 0.5k */
+		t_faw = mv_ddr_speed_bin_timing_get(speed_bin_index, SPEED_BIN_TFAW2K);
+		t_faw = time_to_nclk(t_faw, t_ckclk);
+		t_faw = GET_MAX_VALUE(28, t_faw);
+	}
+
+	t_pd = GET_MAX_VALUE(t_ckclk * 3, mv_ddr_speed_bin_timing_get(speed_bin_index, SPEED_BIN_TPD));
+	t_pd = time_to_nclk(t_pd, t_ckclk);
+
+	t_xpdll = GET_MAX_VALUE(t_ckclk * 10, mv_ddr_speed_bin_timing_get(speed_bin_index, SPEED_BIN_TXPDLL));
+	t_xpdll = time_to_nclk(t_xpdll, t_ckclk);
+
+	t_rrd =	(page_size == 1) ? mv_ddr_speed_bin_timing_get(speed_bin_index,
+						   SPEED_BIN_TRRD1K) :
+		mv_ddr_speed_bin_timing_get(speed_bin_index, SPEED_BIN_TRRD2K);
+	t_rrd = GET_MAX_VALUE(t_ckclk * 4, t_rrd);
+	t_rtp =	GET_MAX_VALUE(t_ckclk * 4, mv_ddr_speed_bin_timing_get(speed_bin_index,
+							   SPEED_BIN_TRTP));
+	t_mod = GET_MAX_VALUE(t_ckclk * 12, 15000);
+	t_wtr = GET_MAX_VALUE(t_ckclk * 4, mv_ddr_speed_bin_timing_get(speed_bin_index,
+							   SPEED_BIN_TWTR));
+	t_ras = time_to_nclk(mv_ddr_speed_bin_timing_get(speed_bin_index,
+						    SPEED_BIN_TRAS),
+				    t_ckclk);
+	t_rcd = time_to_nclk(mv_ddr_speed_bin_timing_get(speed_bin_index,
+						    SPEED_BIN_TRCD),
+				    t_ckclk);
+	t_rp = time_to_nclk(mv_ddr_speed_bin_timing_get(speed_bin_index,
+						   SPEED_BIN_TRP),
+				   t_ckclk);
+	t_wr = time_to_nclk(mv_ddr_speed_bin_timing_get(speed_bin_index,
+						   SPEED_BIN_TWR),
+				   t_ckclk);
+	t_wtr = time_to_nclk(t_wtr, t_ckclk);
+	t_rrd = time_to_nclk(t_rrd, t_ckclk);
+	t_rtp = time_to_nclk(t_rtp, t_ckclk);
+	t_rfc = time_to_nclk(mv_ddr_rfc_get(memory_size) * 1000, t_ckclk);
+	t_mod = time_to_nclk(t_mod, t_ckclk);
+
+	/* SDRAM Timing Low */
+	val = (((t_ras - 1) & SDRAM_TIMING_LOW_TRAS_MASK) << SDRAM_TIMING_LOW_TRAS_OFFS) |
+	      (((t_rcd - 1) & SDRAM_TIMING_LOW_TRCD_MASK) << SDRAM_TIMING_LOW_TRCD_OFFS) |
+	      (((t_rcd - 1) >> SDRAM_TIMING_LOW_TRCD_OFFS & SDRAM_TIMING_HIGH_TRCD_MASK)
+	      << SDRAM_TIMING_HIGH_TRCD_OFFS) |
+	      (((t_rp - 1) & SDRAM_TIMING_LOW_TRP_MASK) << SDRAM_TIMING_LOW_TRP_OFFS) |
+	      (((t_rp - 1) >> SDRAM_TIMING_LOW_TRP_MASK & SDRAM_TIMING_HIGH_TRP_MASK)
+	      << SDRAM_TIMING_HIGH_TRP_OFFS) |
+	      (((t_wr - 1) & SDRAM_TIMING_LOW_TWR_MASK) << SDRAM_TIMING_LOW_TWR_OFFS) |
+	      (((t_wtr - 1) & SDRAM_TIMING_LOW_TWTR_MASK) << SDRAM_TIMING_LOW_TWTR_OFFS) |
+	      ((((t_ras - 1) >> 4) & SDRAM_TIMING_LOW_TRAS_HIGH_MASK) << SDRAM_TIMING_LOW_TRAS_HIGH_OFFS) |
+	      (((t_rrd - 1) & SDRAM_TIMING_LOW_TRRD_MASK) << SDRAM_TIMING_LOW_TRRD_OFFS) |
+	      (((t_rtp - 1) & SDRAM_TIMING_LOW_TRTP_MASK) << SDRAM_TIMING_LOW_TRTP_OFFS);
+
+	mask = (SDRAM_TIMING_LOW_TRAS_MASK << SDRAM_TIMING_LOW_TRAS_OFFS) |
+	       (SDRAM_TIMING_LOW_TRCD_MASK << SDRAM_TIMING_LOW_TRCD_OFFS) |
+	       (SDRAM_TIMING_HIGH_TRCD_MASK << SDRAM_TIMING_HIGH_TRCD_OFFS) |
+	       (SDRAM_TIMING_LOW_TRP_MASK << SDRAM_TIMING_LOW_TRP_OFFS) |
+	       (SDRAM_TIMING_HIGH_TRP_MASK << SDRAM_TIMING_HIGH_TRP_OFFS) |
+	       (SDRAM_TIMING_LOW_TWR_MASK << SDRAM_TIMING_LOW_TWR_OFFS) |
+	       (SDRAM_TIMING_LOW_TWTR_MASK << SDRAM_TIMING_LOW_TWTR_OFFS) |
+	       (SDRAM_TIMING_LOW_TRAS_HIGH_MASK << SDRAM_TIMING_LOW_TRAS_HIGH_OFFS) |
+	       (SDRAM_TIMING_LOW_TRRD_MASK << SDRAM_TIMING_LOW_TRRD_OFFS) |
+	       (SDRAM_TIMING_LOW_TRTP_MASK << SDRAM_TIMING_LOW_TRTP_OFFS);
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       SDRAM_TIMING_LOW_REG, val, mask));
+
+	/* SDRAM Timing High */
+	val = 0;
+	mask = 0;
+
+	val = (((t_rfc - 1) & SDRAM_TIMING_HIGH_TRFC_MASK) << SDRAM_TIMING_HIGH_TRFC_OFFS) |
+	      ((t_r2r & SDRAM_TIMING_HIGH_TR2R_MASK) << SDRAM_TIMING_HIGH_TR2R_OFFS) |
+	      ((t_r2w_w2r & SDRAM_TIMING_HIGH_TR2W_W2R_MASK) << SDRAM_TIMING_HIGH_TR2W_W2R_OFFS) |
+	      ((t_w2w & SDRAM_TIMING_HIGH_TW2W_MASK) << SDRAM_TIMING_HIGH_TW2W_OFFS) |
+	      ((((t_rfc - 1) >> 7) & SDRAM_TIMING_HIGH_TRFC_HIGH_MASK) << SDRAM_TIMING_HIGH_TRFC_HIGH_OFFS) |
+	      ((t_r2r_high & SDRAM_TIMING_HIGH_TR2R_HIGH_MASK) << SDRAM_TIMING_HIGH_TR2R_HIGH_OFFS) |
+	      ((t_r2w_w2r_high & SDRAM_TIMING_HIGH_TR2W_W2R_HIGH_MASK) << SDRAM_TIMING_HIGH_TR2W_W2R_HIGH_OFFS) |
+	      (((t_mod - 1) & SDRAM_TIMING_HIGH_TMOD_MASK) << SDRAM_TIMING_HIGH_TMOD_OFFS) |
+	      ((((t_mod - 1) >> 4) & SDRAM_TIMING_HIGH_TMOD_HIGH_MASK) << SDRAM_TIMING_HIGH_TMOD_HIGH_OFFS);
+
+	mask = (SDRAM_TIMING_HIGH_TRFC_MASK << SDRAM_TIMING_HIGH_TRFC_OFFS) |
+	       (SDRAM_TIMING_HIGH_TR2R_MASK << SDRAM_TIMING_HIGH_TR2R_OFFS) |
+	       (SDRAM_TIMING_HIGH_TR2W_W2R_MASK << SDRAM_TIMING_HIGH_TR2W_W2R_OFFS) |
+	       (SDRAM_TIMING_HIGH_TW2W_MASK << SDRAM_TIMING_HIGH_TW2W_OFFS) |
+	       (SDRAM_TIMING_HIGH_TRFC_HIGH_MASK << SDRAM_TIMING_HIGH_TRFC_HIGH_OFFS) |
+	       (SDRAM_TIMING_HIGH_TR2R_HIGH_MASK << SDRAM_TIMING_HIGH_TR2R_HIGH_OFFS) |
+	       (SDRAM_TIMING_HIGH_TR2W_W2R_HIGH_MASK << SDRAM_TIMING_HIGH_TR2W_W2R_HIGH_OFFS) |
+	       (SDRAM_TIMING_HIGH_TMOD_MASK << SDRAM_TIMING_HIGH_TMOD_OFFS) |
+	       (SDRAM_TIMING_HIGH_TMOD_HIGH_MASK << SDRAM_TIMING_HIGH_TMOD_HIGH_OFFS);
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       SDRAM_TIMING_HIGH_REG, val, mask));
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       SDRAM_CFG_REG,
+				       refresh_interval_cnt << REFRESH_OFFS,
+				       REFRESH_MASK << REFRESH_OFFS));
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       SDRAM_ADDR_CTRL_REG, (t_faw - 1) << T_FAW_OFFS,
+				       T_FAW_MASK << T_FAW_OFFS));
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, DDR_TIMING_REG,
+				       (t_pd - 1) << DDR_TIMING_TPD_OFFS |
+				       (t_xpdll - 1) << DDR_TIMING_TXPDLL_OFFS,
+				       DDR_TIMING_TPD_MASK << DDR_TIMING_TPD_OFFS |
+				       DDR_TIMING_TXPDLL_MASK << DDR_TIMING_TXPDLL_OFFS));
+
+
+	return MV_OK;
+}
+
+
+/*
+ * Write CS Result
+ */
+int ddr3_tip_write_cs_result(u32 dev_num, u32 offset)
+{
+	u32 if_id, bus_num, cs_bitmask, data_val, cs_num;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_num = 0; bus_num < octets_per_if_num;
+		     bus_num++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_num);
+			cs_bitmask =
+				tm->interface_params[if_id].
+				as_bus_params[bus_num].cs_bitmask;
+			if (cs_bitmask != effective_cs) {
+				cs_num = GET_CS_FROM_MASK(cs_bitmask);
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_num,
+						  DDR_PHY_DATA,
+						  offset +
+						  (effective_cs * 0x4),
+						  &data_val);
+				ddr3_tip_bus_write(dev_num,
+						   ACCESS_TYPE_UNICAST,
+						   if_id,
+						   ACCESS_TYPE_UNICAST,
+						   bus_num, DDR_PHY_DATA,
+						   offset +
+						   (cs_num * 0x4),
+						   data_val);
+			}
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Write MRS
+ */
+int ddr3_tip_write_mrs_cmd(u32 dev_num, u32 *cs_mask_arr, enum mr_number mr_num, u32 data, u32 mask)
+{
+	u32 if_id;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       PARAM_NOT_CARE, mr_data[mr_num].reg_addr, data, mask));
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      SDRAM_OP_REG,
+			      (cs_mask_arr[if_id] << 8) | mr_data[mr_num].cmd, 0xf1f));
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (ddr3_tip_if_polling(dev_num, ACCESS_TYPE_UNICAST, if_id, 0,
+					0x1f, SDRAM_OP_REG,
+					MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("write_mrs_cmd: Poll cmd fail"));
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Reset XSB Read FIFO
+ */
+int ddr3_tip_reset_fifo_ptr(u32 dev_num)
+{
+	u32 if_id = 0;
+
+	/* Configure PHY reset value to 0 in order to "clean" the FIFO */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, 0x15c8, 0, 0xff000000));
+	/*
+	 * Move PHY to RL mode (only in RL mode the PHY overrides FIFO values
+	 * during FIFO reset)
+	 */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, TRAINING_SW_2_REG,
+				       0x1, 0x9));
+	/* In order that above configuration will influence the PHY */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, 0x15b0,
+				       0x80000000, 0x80000000));
+	/* Reset read fifo assertion */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, 0x1400, 0, 0x40000000));
+	/* Reset read fifo deassertion */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, 0x1400,
+				       0x40000000, 0x40000000));
+	/* Move PHY back to functional mode */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, TRAINING_SW_2_REG,
+				       0x8, 0x9));
+	/* Stop training machine */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       if_id, 0x15b4, 0x10000, 0x10000));
+
+	return MV_OK;
+}
+
+/*
+ * Reset Phy registers
+ */
+int ddr3_tip_ddr3_reset_phy_regs(u32 dev_num)
+{
+	u32 if_id, phy_id, cs;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (phy_id = 0; phy_id < octets_per_if_num;
+		     phy_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, phy_id);
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST,
+				      if_id, ACCESS_TYPE_UNICAST,
+				      phy_id, DDR_PHY_DATA,
+				      WL_PHY_REG(effective_cs),
+				      phy_reg0_val));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      RL_PHY_REG(effective_cs),
+				      phy_reg2_val));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      CRX_PHY_REG(effective_cs), phy_reg3_val));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      CTX_PHY_REG(effective_cs), phy_reg1_val));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      PBS_TX_BCAST_PHY_REG(effective_cs), 0x0));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      PBS_RX_BCAST_PHY_REG(effective_cs), 0));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      PBS_TX_PHY_REG(effective_cs, DQSP_PAD), 0));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      PBS_RX_PHY_REG(effective_cs, DQSP_PAD), 0));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      PBS_TX_PHY_REG(effective_cs, DQSN_PAD), 0));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				      PBS_RX_PHY_REG(effective_cs, DQSN_PAD), 0));
+		}
+	}
+
+	/* Set Receiver Calibration value */
+	for (cs = 0; cs < MAX_CS_NUM; cs++) {
+		/* PHY register 0xdb bits[5:0] - configure to 63 */
+		CHECK_STATUS(ddr3_tip_bus_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      DDR_PHY_DATA, VREF_BCAST_PHY_REG(cs), 63));
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Restore Dunit registers
+ */
+int ddr3_tip_restore_dunit_regs(u32 dev_num)
+{
+	u32 index_cnt;
+
+	mv_ddr_set_calib_controller();
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       PARAM_NOT_CARE, MAIN_PADS_CAL_MACH_CTRL_REG,
+				       0x1, 0x1));
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       PARAM_NOT_CARE, MAIN_PADS_CAL_MACH_CTRL_REG,
+				       calibration_update_control << 3,
+				       0x3 << 3));
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST,
+				       PARAM_NOT_CARE,
+				       ODPG_WR_RD_MODE_ENA_REG,
+				       0xffff, MASK_ALL_BITS));
+
+	for (index_cnt = 0; index_cnt < ARRAY_SIZE(odpg_default_value);
+	     index_cnt++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      odpg_default_value[index_cnt].reg_addr,
+			      odpg_default_value[index_cnt].reg_data,
+			      odpg_default_value[index_cnt].reg_mask));
+	}
+
+	return MV_OK;
+}
+
+int ddr3_tip_adll_regs_bypass(u32 dev_num, u32 reg_val1, u32 reg_val2)
+{
+	u32 if_id, phy_id;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (phy_id = 0; phy_id < octets_per_if_num; phy_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, phy_id);
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				     ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				     CTX_PHY_REG(effective_cs), reg_val1));
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				     ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA,
+				     PBS_TX_BCAST_PHY_REG(effective_cs), reg_val2));
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Auto tune main flow
+ */
+static int ddr3_tip_ddr3_training_main_flow(u32 dev_num)
+{
+/* TODO: enable this functionality for other platforms */
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X)
+	struct init_cntr_param init_cntr_prm;
+#endif
+	int ret = MV_OK;
+	int adll_bypass_flag = 0;
+	u32 if_id;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	enum mv_ddr_freq freq = tm->interface_params[0].memory_freq;
+	unsigned int *freq_tbl = mv_ddr_freq_tbl_get();
+
+#ifdef DDR_VIEWER_TOOL
+	if (debug_training == DEBUG_LEVEL_TRACE) {
+		CHECK_STATUS(print_device_info((u8)dev_num));
+	}
+#endif
+
+	ddr3_tip_validate_algo_components(dev_num);
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		CHECK_STATUS(ddr3_tip_ddr3_reset_phy_regs(dev_num));
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	freq_tbl[MV_DDR_FREQ_LOW_FREQ] = dfs_low_freq;
+
+	if (is_pll_before_init != 0) {
+		for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			config_func_info[dev_num].tip_set_freq_divider_func(
+				(u8)dev_num, if_id, freq);
+		}
+	}
+
+/* TODO: enable this functionality for other platforms */
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X)
+	if (is_adll_calib_before_init != 0) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("with adll calib before init\n"));
+		adll_calibration(dev_num, ACCESS_TYPE_MULTICAST, 0, freq);
+	}
+
+	if (is_reg_dump != 0) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("Dump before init controller\n"));
+		ddr3_tip_reg_dump(dev_num);
+	}
+
+	if (mask_tune_func & INIT_CONTROLLER_MASK_BIT) {
+		training_stage = INIT_CONTROLLER;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("INIT_CONTROLLER_MASK_BIT\n"));
+		init_cntr_prm.do_mrs_phy = 1;
+		init_cntr_prm.is_ctrl64_bit = 0;
+		init_cntr_prm.init_phy = 1;
+		init_cntr_prm.msys_init = 0;
+		ret = hws_ddr3_tip_init_controller(dev_num, &init_cntr_prm);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("hws_ddr3_tip_init_controller failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+#endif
+
+	ret = adll_calibration(dev_num, ACCESS_TYPE_MULTICAST, 0, freq);
+	if (ret != MV_OK) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+			("adll_calibration failure\n"));
+		if (debug_mode == 0)
+			return MV_FAIL;
+	}
+
+	if (mask_tune_func & SET_LOW_FREQ_MASK_BIT) {
+		training_stage = SET_LOW_FREQ;
+
+		for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+			ddr3_tip_adll_regs_bypass(dev_num, 0, 0x1f);
+			adll_bypass_flag = 1;
+		}
+		effective_cs = 0;
+
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("SET_LOW_FREQ_MASK_BIT %d\n",
+				   freq_tbl[low_freq]));
+		ret = ddr3_tip_freq_set(dev_num, ACCESS_TYPE_MULTICAST,
+					PARAM_NOT_CARE, low_freq);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_freq_set failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & WRITE_LEVELING_LF_MASK_BIT) {
+		training_stage = WRITE_LEVELING_LF;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+			("WRITE_LEVELING_LF_MASK_BIT\n"));
+		ret = ddr3_tip_dynamic_write_leveling(dev_num, 1);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				("ddr3_tip_dynamic_write_leveling LF failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & LOAD_PATTERN_MASK_BIT) {
+			training_stage = LOAD_PATTERN;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("LOAD_PATTERN_MASK_BIT #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_load_all_pattern_to_mem(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_load_all_pattern_to_mem failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+
+	if (adll_bypass_flag == 1) {
+		for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+			ddr3_tip_adll_regs_bypass(dev_num, phy_reg1_val, 0);
+			adll_bypass_flag = 0;
+		}
+	}
+
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	if (mask_tune_func & SET_MEDIUM_FREQ_MASK_BIT) {
+		training_stage = SET_MEDIUM_FREQ;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("SET_MEDIUM_FREQ_MASK_BIT %d\n",
+				   freq_tbl[medium_freq]));
+		ret =
+			ddr3_tip_freq_set(dev_num, ACCESS_TYPE_MULTICAST,
+					  PARAM_NOT_CARE, medium_freq);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_freq_set failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & WRITE_LEVELING_MASK_BIT) {
+		training_stage = WRITE_LEVELING;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("WRITE_LEVELING_MASK_BIT\n"));
+		if ((rl_mid_freq_wa == 0) || (freq_tbl[medium_freq] == 533)) {
+			ret = ddr3_tip_dynamic_write_leveling(dev_num, 0);
+		} else {
+			/* Use old WL */
+			ret = ddr3_tip_legacy_dynamic_write_leveling(dev_num);
+		}
+
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_dynamic_write_leveling failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & LOAD_PATTERN_2_MASK_BIT) {
+			training_stage = LOAD_PATTERN_2;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("LOAD_PATTERN_2_MASK_BIT CS #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_load_all_pattern_to_mem(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_load_all_pattern_to_mem failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	if (mask_tune_func & READ_LEVELING_MASK_BIT) {
+		training_stage = READ_LEVELING;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("READ_LEVELING_MASK_BIT\n"));
+		if ((rl_mid_freq_wa == 0) || (freq_tbl[medium_freq] == 533)) {
+			ret = ddr3_tip_dynamic_read_leveling(dev_num, medium_freq);
+		} else {
+			/* Use old RL */
+			ret = ddr3_tip_legacy_dynamic_read_leveling(dev_num);
+		}
+
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_dynamic_read_leveling failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & WRITE_LEVELING_SUPP_MASK_BIT) {
+		training_stage = WRITE_LEVELING_SUPP;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("WRITE_LEVELING_SUPP_MASK_BIT\n"));
+		ret = ddr3_tip_dynamic_write_leveling_supp(dev_num);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_dynamic_write_leveling_supp failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & PBS_RX_MASK_BIT) {
+			training_stage = PBS_RX;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("PBS_RX_MASK_BIT CS #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_pbs_rx(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_pbs_rx failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & PBS_TX_MASK_BIT) {
+			training_stage = PBS_TX;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("PBS_TX_MASK_BIT CS #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_pbs_tx(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_pbs_tx failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	if (mask_tune_func & SET_TARGET_FREQ_MASK_BIT) {
+		training_stage = SET_TARGET_FREQ;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("SET_TARGET_FREQ_MASK_BIT %d\n",
+				   freq_tbl[tm->
+					    interface_params[first_active_if].
+					    memory_freq]));
+		ret = ddr3_tip_freq_set(dev_num, ACCESS_TYPE_MULTICAST,
+					PARAM_NOT_CARE,
+					tm->interface_params[first_active_if].
+					memory_freq);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_freq_set failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & WRITE_LEVELING_TF_MASK_BIT) {
+		training_stage = WRITE_LEVELING_TF;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("WRITE_LEVELING_TF_MASK_BIT\n"));
+		ret = ddr3_tip_dynamic_write_leveling(dev_num, 0);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_dynamic_write_leveling TF failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & LOAD_PATTERN_HIGH_MASK_BIT) {
+		training_stage = LOAD_PATTERN_HIGH;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("LOAD_PATTERN_HIGH\n"));
+		ret = ddr3_tip_load_all_pattern_to_mem(dev_num);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_load_all_pattern_to_mem failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & READ_LEVELING_TF_MASK_BIT) {
+		training_stage = READ_LEVELING_TF;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("READ_LEVELING_TF_MASK_BIT\n"));
+		ret = ddr3_tip_dynamic_read_leveling(dev_num, tm->
+						     interface_params[first_active_if].
+						     memory_freq);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("ddr3_tip_dynamic_read_leveling TF failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & RL_DQS_BURST_MASK_BIT) {
+		training_stage = READ_LEVELING_TF;
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("RL_DQS_BURST_MASK_BIT\n"));
+		ret = mv_ddr_rl_dqs_burst(0, 0, tm->interface_params[0].memory_freq);
+		if (is_reg_dump != 0)
+			ddr3_tip_reg_dump(dev_num);
+		if (ret != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("mv_ddr_rl_dqs_burst TF failure\n"));
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+
+	if (mask_tune_func & DM_PBS_TX_MASK_BIT) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("DM_PBS_TX_MASK_BIT\n"));
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & VREF_CALIBRATION_MASK_BIT) {
+			training_stage = VREF_CALIBRATION;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("VREF\n"));
+			ret = ddr3_tip_vref(dev_num);
+			if (is_reg_dump != 0) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("VREF Dump\n"));
+				ddr3_tip_reg_dump(dev_num);
+			}
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_vref failure\n"));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & CENTRALIZATION_RX_MASK_BIT) {
+			training_stage = CENTRALIZATION_RX;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("CENTRALIZATION_RX_MASK_BIT CS #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_centralization_rx(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_centralization_rx failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & WRITE_LEVELING_SUPP_TF_MASK_BIT) {
+			training_stage = WRITE_LEVELING_SUPP_TF;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("WRITE_LEVELING_SUPP_TF_MASK_BIT CS #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_dynamic_write_leveling_supp(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_dynamic_write_leveling_supp TF failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		if (mask_tune_func & CENTRALIZATION_TX_MASK_BIT) {
+			training_stage = CENTRALIZATION_TX;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("CENTRALIZATION_TX_MASK_BIT CS #%d\n",
+					   effective_cs));
+			ret = ddr3_tip_centralization_tx(dev_num);
+			if (is_reg_dump != 0)
+				ddr3_tip_reg_dump(dev_num);
+			if (ret != MV_OK) {
+				DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+						  ("ddr3_tip_centralization_tx failure CS #%d\n",
+						   effective_cs));
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("restore registers to default\n"));
+	/* restore register values */
+	CHECK_STATUS(ddr3_tip_restore_dunit_regs(dev_num));
+
+	if (is_reg_dump != 0)
+		ddr3_tip_reg_dump(dev_num);
+
+	return MV_OK;
+}
+
+/*
+ * DDR3 Dynamic training flow
+ */
+static int ddr3_tip_ddr3_auto_tune(u32 dev_num)
+{
+	int status;
+	u32 if_id, stage;
+	int is_if_fail = 0, is_auto_tune_fail = 0;
+
+	training_stage = INIT_CONTROLLER;
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		for (stage = 0; stage < MAX_STAGE_LIMIT; stage++)
+			training_result[stage][if_id] = NO_TEST_DONE;
+	}
+
+	status = ddr3_tip_ddr3_training_main_flow(dev_num);
+
+	/* activate XSB test */
+	if (xsb_validate_type != 0) {
+		run_xsb_test(dev_num, xsb_validation_base_address, 1, 1,
+			     0x1024);
+	}
+
+	if (is_reg_dump != 0)
+		ddr3_tip_reg_dump(dev_num);
+
+	/* print log */
+	CHECK_STATUS(ddr3_tip_print_log(dev_num, window_mem_addr));
+
+#ifndef EXCLUDE_DEBUG_PRINTS
+	if (status != MV_OK) {
+		CHECK_STATUS(ddr3_tip_print_stability_log(dev_num));
+	}
+#endif /* EXCLUDE_DEBUG_PRINTS */
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		is_if_fail = 0;
+		for (stage = 0; stage < MAX_STAGE_LIMIT; stage++) {
+			if (training_result[stage][if_id] == TEST_FAILED)
+				is_if_fail = 1;
+		}
+		if (is_if_fail == 1) {
+			is_auto_tune_fail = 1;
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+					  ("Auto Tune failed for IF %d\n",
+					   if_id));
+		}
+	}
+
+	if (((status == MV_FAIL) && (is_auto_tune_fail == 0)) ||
+	    ((status == MV_OK) && (is_auto_tune_fail == 1))) {
+		/*
+		 * If MainFlow result and trainingResult DB not in sync,
+		 * issue warning (caused by no update of trainingResult DB
+		 * when failed)
+		 */
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO,
+				  ("Warning: Algorithm return value and Result DB"
+				   "are not synced (status 0x%x  result DB %d)\n",
+				   status, is_auto_tune_fail));
+	}
+
+	if ((status != MV_OK) || (is_auto_tune_fail == 1))
+		return MV_FAIL;
+	else
+		return MV_OK;
+}
+
+/*
+ * Enable init sequence
+ */
+int ddr3_tip_enable_init_sequence(u32 dev_num)
+{
+	int is_fail = 0;
+	u32 if_id = 0, mem_mask = 0, bus_index = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* Enable init sequence */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, 0,
+				       SDRAM_INIT_CTRL_REG, 0x1, 0x1));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x1,
+		     SDRAM_INIT_CTRL_REG,
+		     MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("polling failed IF %d\n",
+					   if_id));
+			is_fail = 1;
+			continue;
+		}
+
+		mem_mask = 0;
+		for (bus_index = 0; bus_index < octets_per_if_num;
+		     bus_index++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_index);
+			mem_mask |=
+				tm->interface_params[if_id].
+				as_bus_params[bus_index].mirror_enable_bitmask;
+		}
+
+		if (mem_mask != 0) {
+			/* Disable Multi CS */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_MULTICAST,
+				      if_id, DUAL_DUNIT_CFG_REG, 1 << 3,
+				      1 << 3));
+		}
+	}
+
+	return (is_fail == 0) ? MV_OK : MV_FAIL;
+}
+
+int ddr3_tip_register_dq_table(u32 dev_num, u32 *table)
+{
+	dq_map_table = table;
+
+	return MV_OK;
+}
+
+/*
+ * Check if pup search is locked
+ */
+int ddr3_tip_is_pup_lock(u32 *pup_buf, enum hws_training_result read_mode)
+{
+	u32 bit_start = 0, bit_end = 0, bit_id;
+
+	if (read_mode == RESULT_PER_BIT) {
+		bit_start = 0;
+		bit_end = BUS_WIDTH_IN_BITS - 1;
+	} else {
+		bit_start = 0;
+		bit_end = 0;
+	}
+
+	for (bit_id = bit_start; bit_id <= bit_end; bit_id++) {
+		if (GET_LOCK_RESULT(pup_buf[bit_id]) == 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Get minimum buffer value
+ */
+u8 ddr3_tip_get_buf_min(u8 *buf_ptr)
+{
+	u8 min_val = 0xff;
+	u8 cnt = 0;
+
+	for (cnt = 0; cnt < BUS_WIDTH_IN_BITS; cnt++) {
+		if (buf_ptr[cnt] < min_val)
+			min_val = buf_ptr[cnt];
+	}
+
+	return min_val;
+}
+
+/*
+ * Get maximum buffer value
+ */
+u8 ddr3_tip_get_buf_max(u8 *buf_ptr)
+{
+	u8 max_val = 0;
+	u8 cnt = 0;
+
+	for (cnt = 0; cnt < BUS_WIDTH_IN_BITS; cnt++) {
+		if (buf_ptr[cnt] > max_val)
+			max_val = buf_ptr[cnt];
+	}
+
+	return max_val;
+}
+
+/*
+ * The following functions return memory parameters:
+ * bus and device width, device size
+ */
+
+u32 hws_ddr3_get_bus_width(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	return (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) ==
+		1) ? 16 : 32;
+}
+
+u32 hws_ddr3_get_device_width(u32 if_id)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	return (tm->interface_params[if_id].bus_width ==
+		MV_DDR_DEV_WIDTH_8BIT) ? 8 : 16;
+}
+
+u32 hws_ddr3_get_device_size(u32 if_id)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (tm->interface_params[if_id].memory_size >=
+	    MV_DDR_DIE_CAP_LAST) {
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("Error: Wrong device size of Cs: %d",
+				   tm->interface_params[if_id].memory_size));
+		return 0;
+	} else {
+		return 1 << tm->interface_params[if_id].memory_size;
+	}
+}
+
+int hws_ddr3_calc_mem_cs_size(u32 if_id, u32 cs, u32 *cs_size)
+{
+	u32 cs_mem_size, dev_size;
+
+	dev_size = hws_ddr3_get_device_size(if_id);
+	if (dev_size != 0) {
+		cs_mem_size = ((hws_ddr3_get_bus_width() /
+				hws_ddr3_get_device_width(if_id)) * dev_size);
+
+		/* the calculated result in Gbytex16 to avoid float using */
+
+		if (cs_mem_size == 2) {
+			*cs_size = _128M;
+		} else if (cs_mem_size == 4) {
+			*cs_size = _256M;
+		} else if (cs_mem_size == 8) {
+			*cs_size = _512M;
+		} else if (cs_mem_size == 16) {
+			*cs_size = _1G;
+		} else if (cs_mem_size == 32) {
+			*cs_size = _2G;
+		} else {
+			DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+					  ("Error: Wrong Memory size of Cs: %d", cs));
+			return MV_FAIL;
+		}
+		return MV_OK;
+	} else {
+		return MV_FAIL;
+	}
+}
+
+int hws_ddr3_cs_base_adr_calc(u32 if_id, u32 cs, u32 *cs_base_addr)
+{
+	u32 cs_mem_size = 0;
+#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE
+	u32 physical_mem_size;
+	u32 max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE;
+#endif
+
+	if (hws_ddr3_calc_mem_cs_size(if_id, cs, &cs_mem_size) != MV_OK)
+		return MV_FAIL;
+
+#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	/*
+	 * if number of address pins doesn't allow to use max mem size that
+	 * is defined in topology mem size is defined by
+	 * DEVICE_MAX_DRAM_ADDRESS_SIZE
+	 */
+	physical_mem_size = mem_size[tm->interface_params[0].memory_size];
+
+	if (hws_ddr3_get_device_width(cs) == 16) {
+		/*
+		 * 16bit mem device can be twice more - no need in less
+		 * significant pin
+		 */
+		max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE * 2;
+	}
+
+	if (physical_mem_size > max_mem_size) {
+		cs_mem_size = max_mem_size *
+			(hws_ddr3_get_bus_width() /
+			 hws_ddr3_get_device_width(if_id));
+		DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR,
+				  ("Updated Physical Mem size is from 0x%x to %x\n",
+				   physical_mem_size,
+				   DEVICE_MAX_DRAM_ADDRESS_SIZE));
+	}
+#endif
+
+	/* calculate CS base addr */
+	*cs_base_addr = ((cs_mem_size) * cs) & 0xffff0000;
+
+	return MV_OK;
+}
+
+/* TODO: consider to move to misl phy driver */
+enum {
+	MISL_PHY_DRV_OHM_30 = 0xf,
+	MISL_PHY_DRV_OHM_48 = 0xa,
+	MISL_PHY_DRV_OHM_80 = 0x6,
+	MISL_PHY_DRV_OHM_120 = 0x4
+};
+
+enum {
+	MISL_PHY_ODT_OHM_60 = 0x8,
+	MISL_PHY_ODT_OHM_80 = 0x6,
+	MISL_PHY_ODT_OHM_120 = 0x4,
+	MISL_PHY_ODT_OHM_240 = 0x2
+};
+
+static unsigned int mv_ddr_misl_phy_drv_calc(unsigned int cfg)
+{
+	unsigned int val;
+
+	switch (cfg) {
+	case MV_DDR_OHM_30:
+		val = MISL_PHY_DRV_OHM_30;
+		break;
+	case MV_DDR_OHM_48:
+		val = MISL_PHY_DRV_OHM_48;
+		break;
+	case MV_DDR_OHM_80:
+		val = MISL_PHY_DRV_OHM_80;
+		break;
+	case MV_DDR_OHM_120:
+		val = MISL_PHY_DRV_OHM_120;
+		break;
+	default:
+		val = PARAM_UNDEFINED;
+	}
+
+	return val;
+}
+
+static unsigned int mv_ddr_misl_phy_odt_calc(unsigned int cfg)
+{
+	unsigned int val;
+
+	switch (cfg) {
+	case MV_DDR_OHM_60:
+		val = MISL_PHY_ODT_OHM_60;
+		break;
+	case MV_DDR_OHM_80:
+		val = MISL_PHY_ODT_OHM_80;
+		break;
+	case MV_DDR_OHM_120:
+		val = MISL_PHY_ODT_OHM_120;
+		break;
+	case MV_DDR_OHM_240:
+		val = MISL_PHY_ODT_OHM_240;
+		break;
+	default:
+		val = PARAM_UNDEFINED;
+	}
+
+	return val;
+}
+
+unsigned int mv_ddr_misl_phy_drv_data_p_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int drv_data_p = mv_ddr_misl_phy_drv_calc(tm->edata.phy_edata.drv_data_p);
+
+	if (drv_data_p == PARAM_UNDEFINED)
+		printf("error: %s: unsupported drv_data_p parameter found\n", __func__);
+
+	return drv_data_p;
+}
+
+unsigned int mv_ddr_misl_phy_drv_data_n_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int drv_data_n = mv_ddr_misl_phy_drv_calc(tm->edata.phy_edata.drv_data_n);
+
+	if (drv_data_n == PARAM_UNDEFINED)
+		printf("error: %s: unsupported drv_data_n parameter found\n", __func__);
+
+	return drv_data_n;
+}
+
+unsigned int mv_ddr_misl_phy_drv_ctrl_p_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int drv_ctrl_p = mv_ddr_misl_phy_drv_calc(tm->edata.phy_edata.drv_ctrl_p);
+
+	if (drv_ctrl_p == PARAM_UNDEFINED)
+		printf("error: %s: unsupported drv_ctrl_p parameter found\n", __func__);
+
+	return drv_ctrl_p;
+}
+
+unsigned int mv_ddr_misl_phy_drv_ctrl_n_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int drv_ctrl_n = mv_ddr_misl_phy_drv_calc(tm->edata.phy_edata.drv_ctrl_n);
+
+	if (drv_ctrl_n == PARAM_UNDEFINED)
+		printf("error: %s: unsupported drv_ctrl_n parameter found\n", __func__);
+
+	return drv_ctrl_n;
+}
+
+unsigned int mv_ddr_misl_phy_odt_p_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int cs_num = mv_ddr_cs_num_get();
+	unsigned int odt_p = PARAM_UNDEFINED;
+
+	if (cs_num > 0 && cs_num <= MAX_CS_NUM)
+		odt_p = mv_ddr_misl_phy_odt_calc(tm->edata.phy_edata.odt_p[cs_num - 1]);
+
+	if (odt_p == PARAM_UNDEFINED)
+		printf("error: %s: unsupported odt_p parameter found\n", __func__);
+
+	return odt_p;
+}
+
+unsigned int mv_ddr_misl_phy_odt_n_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int cs_num = mv_ddr_cs_num_get();
+	unsigned int odt_n = PARAM_UNDEFINED;
+
+	if (cs_num > 0 && cs_num <= MAX_CS_NUM)
+		odt_n = mv_ddr_misl_phy_odt_calc(tm->edata.phy_edata.odt_n[cs_num - 1]);
+
+	if (odt_n == PARAM_UNDEFINED)
+		printf("error: %s: unsupported odt_n parameter found\n", __func__);
+
+	return odt_n;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_bist.c b/drivers/ddr/marvell/a38x/ddr3_training_bist.c
new file mode 100644
index 0000000..d388a17
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_bist.c
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_regs.h"
+
+static u32 bist_offset = 32;
+enum hws_pattern sweep_pattern = PATTERN_KILLER_DQ0;
+
+static int ddr3_tip_bist_operation(u32 dev_num,
+				   enum hws_access_type access_type,
+				   u32 if_id,
+				   enum hws_bist_operation oper_type);
+
+/*
+ * BIST activate
+ */
+int ddr3_tip_bist_activate(u32 dev_num, enum hws_pattern pattern,
+			   enum hws_access_type access_type, u32 if_num,
+			   enum hws_dir dir,
+			   enum hws_stress_jump addr_stress_jump,
+			   enum hws_pattern_duration duration,
+			   enum hws_bist_operation oper_type,
+			   u32 offset, u32 cs_num, u32 pattern_addr_length)
+{
+	u32 tx_burst_size;
+	u32 delay_between_burst;
+	u32 rd_mode;
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+
+	/* odpg bist write enable */
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_CTRL_REG,
+			  (ODPG_WRBUF_WR_CTRL_ENA << ODPG_WRBUF_WR_CTRL_OFFS),
+			  (ODPG_WRBUF_WR_CTRL_MASK << ODPG_WRBUF_WR_CTRL_OFFS));
+
+	/* odpg bist read enable/disable */
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_CTRL_REG,
+			  (dir == OPER_READ) ? (ODPG_WRBUF_RD_CTRL_ENA << ODPG_WRBUF_RD_CTRL_OFFS) :
+					       (ODPG_WRBUF_RD_CTRL_DIS << ODPG_WRBUF_RD_CTRL_OFFS),
+			  (ODPG_WRBUF_RD_CTRL_MASK << ODPG_WRBUF_RD_CTRL_OFFS));
+
+	ddr3_tip_load_pattern_to_odpg(0, access_type, 0, pattern, offset);
+
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_BUFFER_SIZE_REG, pattern_addr_length, MASK_ALL_BITS);
+	tx_burst_size = (dir == OPER_WRITE) ?
+		pattern_table[pattern].tx_burst_size : 0;
+	delay_between_burst = (dir == OPER_WRITE) ? 2 : 0;
+	rd_mode = (dir == OPER_WRITE) ? 1 : 0;
+	ddr3_tip_configure_odpg(0, access_type, 0, dir,
+		      pattern_table[pattern].num_of_phases_tx, tx_burst_size,
+		      pattern_table[pattern].num_of_phases_rx,
+		      delay_between_burst,
+		      rd_mode, cs_num, addr_stress_jump, duration);
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_BUFFER_OFFS_REG, offset, MASK_ALL_BITS);
+
+	if (oper_type == BIST_STOP) {
+		ddr3_tip_bist_operation(0, access_type, 0, BIST_STOP);
+	} else {
+		ddr3_tip_bist_operation(0, access_type, 0, BIST_START);
+		if (mv_ddr_is_odpg_done(MAX_POLLING_ITERATIONS) != MV_OK)
+			return MV_FAIL;
+		ddr3_tip_bist_operation(0, access_type, 0, BIST_STOP);
+	}
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS);
+
+	return MV_OK;
+}
+
+/*
+ * BIST read result
+ */
+int ddr3_tip_bist_read_result(u32 dev_num, u32 if_id,
+			      struct bist_result *pst_bist_result)
+{
+	int ret;
+	u32 read_data[MAX_INTERFACE_NUM];
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (IS_IF_ACTIVE(tm->if_act_mask, if_id) == 0)
+		return MV_NOT_SUPPORTED;
+	DEBUG_TRAINING_BIST_ENGINE(DEBUG_LEVEL_TRACE,
+				   ("ddr3_tip_bist_read_result if_id %d\n",
+				    if_id));
+	ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id,
+			       ODPG_DATA_RX_WORD_ERR_DATA_HIGH_REG, read_data,
+			       MASK_ALL_BITS);
+	if (ret != MV_OK)
+		return ret;
+	pst_bist_result->bist_fail_high = read_data[if_id];
+	ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id,
+			       ODPG_DATA_RX_WORD_ERR_DATA_LOW_REG, read_data,
+			       MASK_ALL_BITS);
+	if (ret != MV_OK)
+		return ret;
+	pst_bist_result->bist_fail_low = read_data[if_id];
+
+	ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id,
+			       ODPG_DATA_RX_WORD_ERR_ADDR_REG, read_data,
+			       MASK_ALL_BITS);
+	if (ret != MV_OK)
+		return ret;
+	pst_bist_result->bist_last_fail_addr = read_data[if_id];
+	ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id,
+			       ODPG_DATA_RX_WORD_ERR_CNTR_REG, read_data,
+			       MASK_ALL_BITS);
+	if (ret != MV_OK)
+		return ret;
+	pst_bist_result->bist_error_cnt = read_data[if_id];
+
+	return MV_OK;
+}
+
+/*
+ * BIST flow - Activate & read result
+ */
+int hws_ddr3_run_bist(u32 dev_num, enum hws_pattern pattern, u32 *result,
+		      u32 cs_num)
+{
+	int ret;
+	u32 i = 0;
+	u32 win_base;
+	struct bist_result st_bist_result;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (i = 0; i < MAX_INTERFACE_NUM; i++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, i);
+		hws_ddr3_cs_base_adr_calc(i, cs_num, &win_base);
+		ret = ddr3_tip_bist_activate(dev_num, pattern,
+					     ACCESS_TYPE_UNICAST,
+					     i, OPER_WRITE, STRESS_NONE,
+					     DURATION_SINGLE, BIST_START,
+					     bist_offset + win_base,
+					     cs_num, 15);
+		if (ret != MV_OK) {
+			printf("ddr3_tip_bist_activate failed (0x%x)\n", ret);
+			return ret;
+		}
+
+		ret = ddr3_tip_bist_activate(dev_num, pattern,
+					     ACCESS_TYPE_UNICAST,
+					     i, OPER_READ, STRESS_NONE,
+					     DURATION_SINGLE, BIST_START,
+					     bist_offset + win_base,
+					     cs_num, 15);
+		if (ret != MV_OK) {
+			printf("ddr3_tip_bist_activate failed (0x%x)\n", ret);
+			return ret;
+		}
+
+		ret = ddr3_tip_bist_read_result(dev_num, i, &st_bist_result);
+		if (ret != MV_OK) {
+			printf("ddr3_tip_bist_read_result failed\n");
+			return ret;
+		}
+		result[i] = st_bist_result.bist_error_cnt;
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Set BIST Operation
+ */
+
+static int ddr3_tip_bist_operation(u32 dev_num,
+				   enum hws_access_type access_type,
+				   u32 if_id, enum hws_bist_operation oper_type)
+{
+	if (oper_type == BIST_STOP)
+		mv_ddr_odpg_disable();
+	else
+		mv_ddr_odpg_enable();
+
+	return MV_OK;
+}
+
+/*
+ * Print BIST result
+ */
+void ddr3_tip_print_bist_res(void)
+{
+	u32 dev_num = 0;
+	u32 i;
+	struct bist_result st_bist_result[MAX_INTERFACE_NUM];
+	int res;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (i = 0; i < MAX_INTERFACE_NUM; i++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, i);
+
+		res = ddr3_tip_bist_read_result(dev_num, i, &st_bist_result[i]);
+		if (res != MV_OK) {
+			DEBUG_TRAINING_BIST_ENGINE(
+				DEBUG_LEVEL_ERROR,
+				("ddr3_tip_bist_read_result failed\n"));
+			return;
+		}
+	}
+
+	DEBUG_TRAINING_BIST_ENGINE(
+		DEBUG_LEVEL_INFO,
+		("interface | error_cnt | fail_low | fail_high | fail_addr\n"));
+
+	for (i = 0; i < MAX_INTERFACE_NUM; i++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, i);
+
+		DEBUG_TRAINING_BIST_ENGINE(
+			DEBUG_LEVEL_INFO,
+			("%d |  0x%08x  |  0x%08x  |  0x%08x  | 0x%08x\n",
+			 i, st_bist_result[i].bist_error_cnt,
+			 st_bist_result[i].bist_fail_low,
+			 st_bist_result[i].bist_fail_high,
+			 st_bist_result[i].bist_last_fail_addr));
+	}
+}
+
+enum {
+	PASS,
+	FAIL
+};
+#define TIP_ITERATION_NUM	31
+static int mv_ddr_tip_bist(enum hws_dir dir, u32 val, enum hws_pattern pattern, u32 cs, u32 *result)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	enum hws_training_ip_stat training_result;
+	u16 *reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u32 max_subphy = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+	u32 subphy, read_data;
+
+	ddr3_tip_ip_training(0, ACCESS_TYPE_MULTICAST, 0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			     RESULT_PER_BYTE, HWS_CONTROL_ELEMENT_ADLL, HWS_LOW2HIGH, dir, tm->if_act_mask, val,
+			     TIP_ITERATION_NUM, pattern, EDGE_FP, CS_SINGLE, cs, &training_result);
+
+	for (subphy = 0; subphy < max_subphy; subphy++) {
+		ddr3_tip_if_read(0, ACCESS_TYPE_UNICAST, 0, reg_map[subphy], &read_data, MASK_ALL_BITS);
+		if (((read_data >> BLOCK_STATUS_OFFS) & BLOCK_STATUS_MASK) == BLOCK_STATUS_NOT_LOCKED)
+			*result |= (FAIL << subphy);
+	}
+
+	return MV_OK;
+}
+
+struct interval {
+	u8 *vector;
+	u8 lendpnt;		/* interval's left endpoint */
+	u8 rendpnt;		/* interval's right endpoint */
+	u8 size;		/* interval's size */
+	u8 lmarker;		/* left marker */
+	u8 rmarker;		/* right marker */
+	u8 pass_lendpnt;	/* left endpoint of internal pass interval */
+	u8 pass_rendpnt;	/* right endpoint of internal pass interval */
+};
+
+static int interval_init(u8 *vector, u8 lendpnt, u8 rendpnt,
+			 u8 lmarker, u8 rmarker, struct interval *intrvl)
+{
+	if (intrvl == NULL) {
+		printf("%s: NULL intrvl pointer found\n", __func__);
+		return MV_FAIL;
+	}
+
+	if (vector == NULL) {
+		printf("%s: NULL vector pointer found\n", __func__);
+		return MV_FAIL;
+	}
+	intrvl->vector = vector;
+
+	if (lendpnt >= rendpnt) {
+		printf("%s: incorrect lendpnt and/or rendpnt parameters found\n", __func__);
+		return MV_FAIL;
+	}
+	intrvl->lendpnt = lendpnt;
+	intrvl->rendpnt = rendpnt;
+	intrvl->size = rendpnt - lendpnt + 1;
+
+	if ((lmarker < lendpnt) || (lmarker > rendpnt)) {
+		printf("%s: incorrect lmarker parameter found\n", __func__);
+		return MV_FAIL;
+	}
+	intrvl->lmarker = lmarker;
+
+	if ((rmarker < lmarker) || (rmarker > (intrvl->rendpnt + intrvl->size))) {
+		printf("%s: incorrect rmarker parameter found\n", __func__);
+		return MV_FAIL;
+	}
+	intrvl->rmarker = rmarker;
+
+	return MV_OK;
+}
+static int interval_set(u8 pass_lendpnt, u8 pass_rendpnt, struct interval *intrvl)
+{
+	if (intrvl == NULL) {
+		printf("%s: NULL intrvl pointer found\n", __func__);
+		return MV_FAIL;
+	}
+
+	intrvl->pass_lendpnt = pass_lendpnt;
+	intrvl->pass_rendpnt = pass_rendpnt;
+
+	return MV_OK;
+}
+
+static int interval_proc(struct interval *intrvl)
+{
+	int curr;
+	int pass_lendpnt, pass_rendpnt;
+	int lmt;
+	int fcnt = 0, pcnt = 0;
+
+	if (intrvl == NULL) {
+		printf("%s: NULL intrvl pointer found\n", __func__);
+		return MV_FAIL;
+	}
+
+	/* count fails and passes */
+	curr = intrvl->lendpnt;
+	while (curr <= intrvl->rendpnt) {
+		if (intrvl->vector[curr] == PASS)
+			pcnt++;
+		else
+			fcnt++;
+		curr++;
+	}
+
+	/* check for all fail */
+	if (fcnt == intrvl->size) {
+		printf("%s: no pass found\n", __func__);
+		return MV_FAIL;
+	}
+
+	/* check for all pass */
+	if (pcnt == intrvl->size) {
+		if (interval_set(intrvl->lendpnt, intrvl->rendpnt, intrvl) != MV_OK)
+			return MV_FAIL;
+		return MV_OK;
+	}
+
+	/* proceed with rmarker */
+	curr = intrvl->rmarker;
+	if (intrvl->vector[curr % intrvl->size] == PASS) { /* pass at rmarker */
+		/* search for fail on right */
+		if (intrvl->rmarker > intrvl->rendpnt)
+			lmt = intrvl->rendpnt + intrvl->size;
+		else
+			lmt = intrvl->rmarker + intrvl->size - 1;
+		while ((curr <= lmt) &&
+		       (intrvl->vector[curr % intrvl->size] == PASS))
+			curr++;
+		if (curr > lmt) { /* fail not found */
+			printf("%s: rmarker: fail following pass not found\n", __func__);
+			return MV_FAIL;
+		}
+		/* fail found */
+		pass_rendpnt = curr - 1;
+	} else { /* fail at rmarker */
+		/* search for pass on left */
+		if (intrvl->rmarker > intrvl->rendpnt)
+			lmt = intrvl->rmarker - intrvl->size + 1;
+		else
+			lmt = intrvl->lendpnt;
+		while ((curr >= lmt) &&
+		       (intrvl->vector[curr % intrvl->size] == FAIL))
+			curr--;
+		if (curr < lmt) { /* pass not found */
+			printf("%s: rmarker: pass preceding fail not found\n", __func__);
+			return MV_FAIL;
+		}
+		/* pass found */
+		pass_rendpnt = curr;
+	}
+
+	/* search for fail on left */
+	curr = pass_rendpnt;
+	if (pass_rendpnt > intrvl->rendpnt)
+		lmt =  pass_rendpnt - intrvl->size + 1;
+	else
+		lmt = intrvl->lendpnt;
+	while ((curr >= lmt) &&
+	       (intrvl->vector[curr % intrvl->size] == PASS))
+		curr--;
+	if (curr < lmt) { /* fail not found */
+		printf("%s: rmarker: fail preceding pass not found\n", __func__);
+		return MV_FAIL;
+	}
+	/* fail found */
+	pass_lendpnt = curr + 1;
+	if (interval_set(pass_lendpnt, pass_rendpnt, intrvl) != MV_OK)
+		return MV_FAIL;
+
+	return MV_OK;
+}
+
+#define ADLL_TAPS_PER_PERIOD	64
+int mv_ddr_dm_to_dq_diff_get(u8 vw_sphy_hi_lmt, u8 vw_sphy_lo_lmt, u8 *vw_vector,
+			     int *vw_sphy_hi_diff, int *vw_sphy_lo_diff)
+{
+	struct interval intrvl;
+
+	/* init interval structure */
+	if (interval_init(vw_vector, 0, ADLL_TAPS_PER_PERIOD - 1,
+			  vw_sphy_lo_lmt, vw_sphy_hi_lmt, &intrvl) != MV_OK)
+		return MV_FAIL;
+
+	/* find pass sub-interval */
+	if (interval_proc(&intrvl) != MV_OK)
+		return MV_FAIL;
+
+	/* check for all pass */
+	if ((intrvl.pass_rendpnt == intrvl.rendpnt) &&
+	    (intrvl.pass_lendpnt == intrvl.lendpnt)) {
+		printf("%s: no fail found\n", __func__);
+		return MV_FAIL;
+	}
+
+	*vw_sphy_hi_diff = intrvl.pass_rendpnt - vw_sphy_hi_lmt;
+	*vw_sphy_lo_diff = vw_sphy_lo_lmt - intrvl.pass_lendpnt;
+
+	return MV_OK;
+}
+
+static int mv_ddr_bist_tx(enum hws_access_type access_type)
+{
+	mv_ddr_odpg_done_clr();
+
+	ddr3_tip_bist_operation(0, access_type, 0, BIST_START);
+
+	if (mv_ddr_is_odpg_done(MAX_POLLING_ITERATIONS) != MV_OK)
+		return MV_FAIL;
+
+	ddr3_tip_bist_operation(0, access_type, 0, BIST_STOP);
+
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS);
+
+	return MV_OK;
+}
+
+/* prepare odpg for bist operation */
+#define WR_OP_ODPG_DATA_CMD_BURST_DLY	2
+static int mv_ddr_odpg_bist_prepare(enum hws_pattern pattern, enum hws_access_type access_type,
+			     enum hws_dir dir, enum hws_stress_jump stress_jump_addr,
+			     enum hws_pattern_duration duration, u32 offset, u32 cs,
+			     u32 pattern_addr_len, enum dm_direction dm_dir)
+{
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	u32 tx_burst_size;
+	u32 burst_delay;
+	u32 rd_mode;
+
+	/* odpg bist write enable */
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_CTRL_REG,
+			  (ODPG_WRBUF_WR_CTRL_ENA << ODPG_WRBUF_WR_CTRL_OFFS),
+			  (ODPG_WRBUF_WR_CTRL_MASK << ODPG_WRBUF_WR_CTRL_OFFS));
+
+	/* odpg bist read enable/disable */
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_CTRL_REG,
+			  (dir == OPER_READ) ? (ODPG_WRBUF_RD_CTRL_ENA << ODPG_WRBUF_RD_CTRL_OFFS) :
+					       (ODPG_WRBUF_RD_CTRL_DIS << ODPG_WRBUF_RD_CTRL_OFFS),
+			  (ODPG_WRBUF_RD_CTRL_MASK << ODPG_WRBUF_RD_CTRL_OFFS));
+
+	if (pattern == PATTERN_00 || pattern == PATTERN_FF)
+		ddr3_tip_load_pattern_to_odpg(0, access_type, 0, pattern, offset);
+	else
+		mv_ddr_load_dm_pattern_to_odpg(access_type, pattern, dm_dir);
+
+	ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_BUFFER_SIZE_REG, pattern_addr_len, MASK_ALL_BITS);
+	if (dir == OPER_WRITE) {
+		tx_burst_size = pattern_table[pattern].tx_burst_size;
+		burst_delay = WR_OP_ODPG_DATA_CMD_BURST_DLY;
+		rd_mode = ODPG_MODE_TX;
+	} else {
+		tx_burst_size = 0;
+		burst_delay = 0;
+		rd_mode = ODPG_MODE_RX;
+	}
+	ddr3_tip_configure_odpg(0, access_type, 0, dir, pattern_table[pattern].num_of_phases_tx,
+				tx_burst_size, pattern_table[pattern].num_of_phases_rx, burst_delay,
+				rd_mode, cs, stress_jump_addr, duration);
+
+	return MV_OK;
+}
+
+#define BYTES_PER_BURST_64BIT	0x20
+#define BYTES_PER_BURST_32BIT	0x10
+int mv_ddr_dm_vw_get(enum hws_pattern pattern, u32 cs, u8 *vw_vector)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	u32 adll_tap;
+	u32 wr_ctrl_adll[MAX_BUS_NUM] = {0};
+	u32 rd_ctrl_adll[MAX_BUS_NUM] = {0};
+	u32 subphy;
+	u32 subphy_max = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+	u32 odpg_addr = 0x0;
+	u32 result;
+	u32 idx;
+	/* burst length in bytes */
+	u32 burst_len = (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask) ?
+			BYTES_PER_BURST_64BIT : BYTES_PER_BURST_32BIT);
+
+	/* save dqs values to restore after algorithm's run */
+	ddr3_tip_read_adll_value(0, wr_ctrl_adll, CTX_PHY_REG(cs), MASK_ALL_BITS);
+	ddr3_tip_read_adll_value(0, rd_ctrl_adll, CRX_PHY_REG(cs), MASK_ALL_BITS);
+
+	/* fill memory with base pattern */
+	ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS);
+	mv_ddr_odpg_bist_prepare(pattern, ACCESS_TYPE_UNICAST, OPER_WRITE, STRESS_NONE, DURATION_SINGLE,
+				 bist_offset, cs, pattern_table[pattern].num_of_phases_tx,
+				 (pattern == PATTERN_00) ? DM_DIR_DIRECT : DM_DIR_INVERSE);
+
+	for (adll_tap = 0; adll_tap < ADLL_TAPS_PER_PERIOD; adll_tap++) {
+		/* change target odpg address */
+		odpg_addr = adll_tap * burst_len;
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_BUFFER_OFFS_REG,
+				  odpg_addr, MASK_ALL_BITS);
+
+		ddr3_tip_configure_odpg(0, ACCESS_TYPE_UNICAST, 0, OPER_WRITE,
+					pattern_table[pattern].num_of_phases_tx,
+					pattern_table[pattern].tx_burst_size,
+					pattern_table[pattern].num_of_phases_rx,
+					WR_OP_ODPG_DATA_CMD_BURST_DLY,
+					ODPG_MODE_TX, cs, STRESS_NONE, DURATION_SINGLE);
+
+		/* odpg bist write enable */
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG,
+				  (ODPG_WRBUF_WR_CTRL_ENA << ODPG_WRBUF_WR_CTRL_OFFS),
+				  (ODPG_WRBUF_WR_CTRL_MASK << ODPG_WRBUF_WR_CTRL_OFFS));
+
+		/* odpg bist read disable */
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG,
+				  (ODPG_WRBUF_RD_CTRL_DIS << ODPG_WRBUF_RD_CTRL_OFFS),
+				  (ODPG_WRBUF_RD_CTRL_MASK << ODPG_WRBUF_RD_CTRL_OFFS));
+
+		/* trigger odpg */
+		mv_ddr_bist_tx(ACCESS_TYPE_MULTICAST);
+	}
+
+	/* fill memory with vref pattern to increment addr using odpg bist */
+	mv_ddr_odpg_bist_prepare(PATTERN_VREF, ACCESS_TYPE_UNICAST, OPER_WRITE, STRESS_NONE, DURATION_SINGLE,
+				 bist_offset, cs, pattern_table[pattern].num_of_phases_tx,
+				 (pattern == PATTERN_00) ? DM_DIR_DIRECT : DM_DIR_INVERSE);
+
+	for (adll_tap = 0; adll_tap < ADLL_TAPS_PER_PERIOD; adll_tap++) {
+		ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_MULTICAST, 0,
+				   DDR_PHY_DATA, CTX_PHY_REG(cs), adll_tap);
+		/* change target odpg address */
+		odpg_addr = adll_tap * burst_len;
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_BUFFER_OFFS_REG,
+				  odpg_addr, MASK_ALL_BITS);
+		ddr3_tip_configure_odpg(0, ACCESS_TYPE_UNICAST, 0, OPER_WRITE,
+					pattern_table[pattern].num_of_phases_tx,
+					pattern_table[pattern].tx_burst_size,
+					pattern_table[pattern].num_of_phases_rx,
+					WR_OP_ODPG_DATA_CMD_BURST_DLY,
+					ODPG_MODE_TX, cs, STRESS_NONE, DURATION_SINGLE);
+
+		/* odpg bist write enable */
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG,
+				  (ODPG_WRBUF_WR_CTRL_ENA << ODPG_WRBUF_WR_CTRL_OFFS),
+				  (ODPG_WRBUF_WR_CTRL_MASK << ODPG_WRBUF_WR_CTRL_OFFS));
+
+		/* odpg bist read disable */
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG,
+				  (ODPG_WRBUF_RD_CTRL_DIS << ODPG_WRBUF_RD_CTRL_OFFS),
+				  (ODPG_WRBUF_RD_CTRL_MASK << ODPG_WRBUF_RD_CTRL_OFFS));
+
+		/* trigger odpg */
+		mv_ddr_bist_tx(ACCESS_TYPE_MULTICAST);
+	}
+
+	/* restore subphy's tx adll_tap to its position */
+	for (subphy = 0; subphy < subphy_max; subphy++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy);
+		ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST,
+				   subphy, DDR_PHY_DATA, CTX_PHY_REG(cs),
+				   wr_ctrl_adll[subphy]);
+	}
+
+	/* read and validate bist (comparing with the base pattern) */
+	for (adll_tap = 0; adll_tap < ADLL_TAPS_PER_PERIOD; adll_tap++) {
+		result = 0;
+		odpg_addr = adll_tap * burst_len;
+		/* change addr to fit write */
+		mv_ddr_pattern_start_addr_set(pattern_table, pattern, odpg_addr);
+		mv_ddr_tip_bist(OPER_READ, 0, pattern, 0, &result);
+		for (subphy = 0; subphy < subphy_max; subphy++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy);
+			idx = ADLL_TAPS_PER_PERIOD * subphy + adll_tap;
+			vw_vector[idx] |= ((result >> subphy) & 0x1);
+		}
+	}
+
+	/* restore subphy's rx adll_tap to its position */
+	for (subphy = 0; subphy < subphy_max; subphy++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy);
+		ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST,
+				   subphy, DDR_PHY_DATA, CRX_PHY_REG(cs),
+				   rd_ctrl_adll[subphy]);
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_centralization.c b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c
new file mode 100644
index 0000000..648b37e
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_regs.h"
+
+#define VALIDATE_WIN_LENGTH(e1, e2, maxsize)		\
+	(((e2) + 1 > (e1) + (u8)MIN_WINDOW_SIZE) &&	\
+	 ((e2) + 1 < (e1) + (u8)maxsize))
+#define IS_WINDOW_OUT_BOUNDARY(e1, e2, maxsize)			\
+	(((e1) == 0 && (e2) != 0) ||				\
+	 ((e1) != (maxsize - 1) && (e2) == (maxsize - 1)))
+#define CENTRAL_TX		0
+#define CENTRAL_RX		1
+#define NUM_OF_CENTRAL_TYPES	2
+
+u32 start_pattern = PATTERN_KILLER_DQ0, end_pattern = PATTERN_KILLER_DQ7;
+
+u32 start_if = 0, end_if = (MAX_INTERFACE_NUM - 1);
+u8 bus_end_window[NUM_OF_CENTRAL_TYPES][MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 bus_start_window[NUM_OF_CENTRAL_TYPES][MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 centralization_state[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+static u8 ddr3_tip_special_rx_run_once_flag;
+
+static int ddr3_tip_centralization(u32 dev_num, u32 mode);
+
+/*
+ * Centralization RX Flow
+ */
+int ddr3_tip_centralization_rx(u32 dev_num)
+{
+	CHECK_STATUS(ddr3_tip_special_rx(dev_num));
+	CHECK_STATUS(ddr3_tip_centralization(dev_num, CENTRAL_RX));
+
+	return MV_OK;
+}
+
+/*
+ * Centralization TX Flow
+ */
+int ddr3_tip_centralization_tx(u32 dev_num)
+{
+	CHECK_STATUS(ddr3_tip_centralization(dev_num, CENTRAL_TX));
+
+	return MV_OK;
+}
+
+/*
+ * Centralization Flow
+ */
+static int ddr3_tip_centralization(u32 dev_num, u32 mode)
+{
+	enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM];
+	u32 if_id, pattern_id, bit_id;
+	u8 bus_id;
+	u8 cur_start_win[BUS_WIDTH_IN_BITS];
+	u8 centralization_result[MAX_INTERFACE_NUM][BUS_WIDTH_IN_BITS];
+	u8 cur_end_win[BUS_WIDTH_IN_BITS];
+	u8 current_window[BUS_WIDTH_IN_BITS];
+	u8 opt_window, waste_window, start_window_skew, end_window_skew;
+	u8 final_pup_window[MAX_INTERFACE_NUM][BUS_WIDTH_IN_BITS];
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	enum hws_training_result result_type = RESULT_PER_BIT;
+	enum hws_dir direction;
+	u32 *result[HWS_SEARCH_DIR_LIMIT];
+	u32 reg_phy_off, reg;
+	u8 max_win_size;
+	int lock_success = 1;
+	u8 cur_end_win_min, cur_start_win_max;
+	u32 cs_enable_reg_val[MAX_INTERFACE_NUM];
+	int is_if_fail = 0;
+	enum hws_result *flow_result = ddr3_tip_get_result_ptr(training_stage);
+	u32 pup_win_length = 0;
+	enum hws_search_dir search_dir_id;
+	u8 cons_tap = (mode == CENTRAL_TX) ? (64) : (0);
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* save current cs enable reg val */
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val, MASK_ALL_BITS));
+		/* enable single cs */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, (1 << 3), (1 << 3)));
+	}
+
+	if (mode == CENTRAL_TX) {
+		max_win_size = MAX_WINDOW_SIZE_TX;
+		reg_phy_off = CTX_PHY_REG(effective_cs);
+		direction = OPER_WRITE;
+	} else {
+		max_win_size = MAX_WINDOW_SIZE_RX;
+		reg_phy_off = CRX_PHY_REG(effective_cs);
+		direction = OPER_READ;
+	}
+
+	/* DB initialization */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0;
+		     bus_id < octets_per_if_num; bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			centralization_state[if_id][bus_id] = 0;
+			bus_end_window[mode][if_id][bus_id] =
+				(max_win_size - 1) + cons_tap;
+			bus_start_window[mode][if_id][bus_id] = 0;
+			centralization_result[if_id][bus_id] = 0;
+		}
+	}
+
+	/* start flow */
+	for (pattern_id = start_pattern; pattern_id <= end_pattern;
+	     pattern_id++) {
+		ddr3_tip_ip_training_wrapper(dev_num, ACCESS_TYPE_MULTICAST,
+					     PARAM_NOT_CARE,
+					     ACCESS_TYPE_MULTICAST,
+					     PARAM_NOT_CARE, result_type,
+					     HWS_CONTROL_ELEMENT_ADLL,
+					     PARAM_NOT_CARE, direction,
+					     tm->
+					     if_act_mask, 0x0,
+					     max_win_size - 1,
+					     max_win_size - 1,
+					     pattern_id, EDGE_FPF, CS_SINGLE,
+					     PARAM_NOT_CARE, training_result);
+
+		for (if_id = start_if; if_id <= end_if; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			for (bus_id = 0;
+			     bus_id <= octets_per_if_num - 1;
+			     bus_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+
+				for (search_dir_id = HWS_LOW2HIGH;
+				     search_dir_id <= HWS_HIGH2LOW;
+				     search_dir_id++) {
+					CHECK_STATUS
+						(ddr3_tip_read_training_result
+						 (dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_id,
+						  ALL_BITS_PER_PUP,
+						  search_dir_id,
+						  direction, result_type,
+						  TRAINING_LOAD_OPERATION_UNLOAD,
+						  CS_SINGLE,
+						  &result[search_dir_id],
+						  1, 0, 0));
+					DEBUG_CENTRALIZATION_ENGINE
+						(DEBUG_LEVEL_INFO,
+						 ("%s pat %d IF %d pup %d Regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+						  ((mode ==
+						    CENTRAL_TX) ? "TX" : "RX"),
+						  pattern_id, if_id, bus_id,
+						  result[search_dir_id][0],
+						  result[search_dir_id][1],
+						  result[search_dir_id][2],
+						  result[search_dir_id][3],
+						  result[search_dir_id][4],
+						  result[search_dir_id][5],
+						  result[search_dir_id][6],
+						  result[search_dir_id][7]));
+				}
+
+				for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS;
+				     bit_id++) {
+					/* check if this code is valid for 2 edge, probably not :( */
+					cur_start_win[bit_id] =
+						GET_TAP_RESULT(result
+							       [HWS_LOW2HIGH]
+							       [bit_id],
+							       EDGE_1);
+					cur_end_win[bit_id] =
+						GET_TAP_RESULT(result
+							       [HWS_HIGH2LOW]
+							       [bit_id],
+							       EDGE_1);
+					/* window length */
+					current_window[bit_id] =
+						cur_end_win[bit_id] -
+						cur_start_win[bit_id] + 1;
+					DEBUG_CENTRALIZATION_ENGINE
+						(DEBUG_LEVEL_TRACE,
+						 ("cs %x patern %d IF %d pup %d cur_start_win %d cur_end_win %d current_window %d\n",
+						  effective_cs, pattern_id,
+						  if_id, bus_id,
+						  cur_start_win[bit_id],
+						  cur_end_win[bit_id],
+						  current_window[bit_id]));
+				}
+
+				if ((ddr3_tip_is_pup_lock
+				     (result[HWS_LOW2HIGH], result_type)) &&
+				    (ddr3_tip_is_pup_lock
+				     (result[HWS_HIGH2LOW], result_type))) {
+					/* read result success */
+					DEBUG_CENTRALIZATION_ENGINE
+						(DEBUG_LEVEL_INFO,
+						 ("Pup locked, pat %d IF %d pup %d\n",
+						  pattern_id, if_id, bus_id));
+				} else {
+					/* read result failure */
+					DEBUG_CENTRALIZATION_ENGINE
+						(DEBUG_LEVEL_INFO,
+						 ("fail Lock, pat %d IF %d pup %d\n",
+						  pattern_id, if_id, bus_id));
+					if (centralization_state[if_id][bus_id]
+					    == 1) {
+						/* continue with next pup */
+						DEBUG_CENTRALIZATION_ENGINE
+							(DEBUG_LEVEL_TRACE,
+							 ("continue to next pup %d %d\n",
+							  if_id, bus_id));
+						continue;
+					}
+
+					for (bit_id = 0;
+					     bit_id < BUS_WIDTH_IN_BITS;
+					     bit_id++) {
+						/*
+						 * the next check is relevant
+						 * only when using search
+						 * machine 2 edges
+						 */
+						if (cur_start_win[bit_id] > 0 &&
+						    cur_end_win[bit_id] == 0) {
+							cur_end_win
+								[bit_id] =
+								max_win_size - 1;
+							DEBUG_CENTRALIZATION_ENGINE
+								(DEBUG_LEVEL_TRACE,
+								 ("fail, IF %d pup %d bit %d fail #1\n",
+								  if_id, bus_id,
+								  bit_id));
+							/* the next bit */
+							continue;
+						} else {
+							centralization_state
+								[if_id][bus_id] = 1;
+							DEBUG_CENTRALIZATION_ENGINE
+								(DEBUG_LEVEL_TRACE,
+								 ("fail, IF %d pup %d bit %d fail #2\n",
+								  if_id, bus_id,
+								  bit_id));
+						}
+					}
+
+					if (centralization_state[if_id][bus_id]
+					    == 1) {
+						/* going to next pup */
+						continue;
+					}
+				}	/*bit */
+
+				opt_window =
+					ddr3_tip_get_buf_min(current_window);
+				/* final pup window length */
+				final_pup_window[if_id][bus_id] =
+					ddr3_tip_get_buf_min(cur_end_win) -
+					ddr3_tip_get_buf_max(cur_start_win) +
+					1;
+				waste_window =
+					opt_window -
+					final_pup_window[if_id][bus_id];
+				start_window_skew =
+					ddr3_tip_get_buf_max(cur_start_win) -
+					ddr3_tip_get_buf_min(
+						cur_start_win);
+				end_window_skew =
+					ddr3_tip_get_buf_max(
+						cur_end_win) -
+					ddr3_tip_get_buf_min(
+						cur_end_win);
+				/* min/max updated with pattern change */
+				cur_end_win_min =
+					ddr3_tip_get_buf_min(
+						cur_end_win);
+				cur_start_win_max =
+					ddr3_tip_get_buf_max(
+						cur_start_win);
+				bus_end_window[mode][if_id][bus_id] =
+					GET_MIN(bus_end_window[mode][if_id]
+						[bus_id],
+						cur_end_win_min);
+				bus_start_window[mode][if_id][bus_id] =
+					GET_MAX(bus_start_window[mode][if_id]
+						[bus_id],
+						cur_start_win_max);
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("pat %d IF %d pup %d opt_win %d final_win %d waste_win %d st_win_skew %d end_win_skew %d cur_st_win_max %d cur_end_win_min %d bus_st_win %d bus_end_win %d\n",
+					 pattern_id, if_id, bus_id, opt_window,
+					 final_pup_window[if_id][bus_id],
+					 waste_window, start_window_skew,
+					 end_window_skew,
+					 cur_start_win_max,
+					 cur_end_win_min,
+					 bus_start_window[mode][if_id][bus_id],
+					 bus_end_window[mode][if_id][bus_id]));
+
+				/* check if window is valid */
+				if (ddr3_tip_centr_skip_min_win_check == 0) {
+					if ((VALIDATE_WIN_LENGTH
+					     (bus_start_window[mode][if_id]
+					      [bus_id],
+					      bus_end_window[mode][if_id]
+					      [bus_id],
+					      max_win_size) == 1) ||
+					    (IS_WINDOW_OUT_BOUNDARY
+					     (bus_start_window[mode][if_id]
+					      [bus_id],
+					      bus_end_window[mode][if_id]
+					      [bus_id],
+					      max_win_size) == 1)) {
+						DEBUG_CENTRALIZATION_ENGINE
+							(DEBUG_LEVEL_INFO,
+							 ("win valid, pat %d IF %d pup %d\n",
+							  pattern_id, if_id,
+							  bus_id));
+						/* window is valid */
+					} else {
+						DEBUG_CENTRALIZATION_ENGINE
+							(DEBUG_LEVEL_INFO,
+							 ("fail win, pat %d IF %d pup %d bus_st_win %d bus_end_win %d\n",
+							  pattern_id, if_id, bus_id,
+							  bus_start_window[mode]
+							  [if_id][bus_id],
+							  bus_end_window[mode]
+							  [if_id][bus_id]));
+						centralization_state[if_id]
+							[bus_id] = 1;
+						if (debug_mode == 0) {
+							flow_result[if_id] = TEST_FAILED;
+							return MV_FAIL;
+						}
+					}
+				}	/* ddr3_tip_centr_skip_min_win_check */
+			}	/* pup */
+		}		/* interface */
+	}			/* pattern */
+
+	for (if_id = start_if; if_id <= end_if; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		is_if_fail = 0;
+		flow_result[if_id] = TEST_SUCCESS;
+
+		for (bus_id = 0;
+		     bus_id <= (octets_per_if_num - 1); bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+
+			/* continue only if lock */
+			if (centralization_state[if_id][bus_id] != 1) {
+				if (ddr3_tip_centr_skip_min_win_check == 0)	{
+					if ((bus_end_window
+					     [mode][if_id][bus_id] ==
+					     (max_win_size - 1)) &&
+					    ((bus_end_window
+					      [mode][if_id][bus_id] -
+					      bus_start_window[mode][if_id]
+					      [bus_id]) < MIN_WINDOW_SIZE) &&
+					    ((bus_end_window[mode][if_id]
+					      [bus_id] - bus_start_window
+					      [mode][if_id][bus_id]) > 2)) {
+						/* prevent false lock */
+						/* TBD change to enum */
+						centralization_state
+							[if_id][bus_id] = 2;
+					}
+
+					if ((bus_end_window[mode][if_id][bus_id]
+					     == 0) &&
+					    ((bus_end_window[mode][if_id]
+					      [bus_id] -
+					      bus_start_window[mode][if_id]
+					      [bus_id]) < MIN_WINDOW_SIZE) &&
+					    ((bus_end_window[mode][if_id]
+					      [bus_id] -
+					      bus_start_window[mode][if_id]
+					      [bus_id]) > 2))
+						/*prevent false lock */
+						centralization_state[if_id]
+							[bus_id] = 3;
+				}
+
+				if ((bus_end_window[mode][if_id][bus_id] >
+				     (max_win_size - 1)) && direction ==
+				    OPER_WRITE) {
+					DEBUG_CENTRALIZATION_ENGINE
+						(DEBUG_LEVEL_INFO,
+						 ("Tx special pattern\n"));
+					cons_tap = 64;
+				}
+			}
+
+			/* check states */
+			if (centralization_state[if_id][bus_id] == 3) {
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("SSW - TBD IF %d pup %d\n",
+					 if_id, bus_id));
+				lock_success = 1;
+			} else if (centralization_state[if_id][bus_id] == 2) {
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("SEW - TBD IF %d pup %d\n",
+					 if_id, bus_id));
+				lock_success = 1;
+			} else if (centralization_state[if_id][bus_id] == 0) {
+				lock_success = 1;
+			} else {
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_ERROR,
+					("fail, IF %d pup %d\n",
+					 if_id, bus_id));
+				lock_success = 0;
+			}
+
+			if (lock_success == 1) {
+				centralization_result[if_id][bus_id] =
+					(bus_end_window[mode][if_id][bus_id] +
+					 bus_start_window[mode][if_id][bus_id])
+					/ 2 - cons_tap;
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_TRACE,
+					(" bus_id %d Res= %d\n", bus_id,
+					 centralization_result[if_id][bus_id]));
+				/* copy results to registers  */
+				pup_win_length =
+					bus_end_window[mode][if_id][bus_id] -
+					bus_start_window[mode][if_id][bus_id] +
+					1;
+
+				ddr3_tip_bus_read(dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, bus_id,
+						  DDR_PHY_DATA,
+						  RESULT_PHY_REG +
+						  effective_cs, &reg);
+				reg = (reg & (~0x1f <<
+					      ((mode == CENTRAL_TX) ?
+					       (RESULT_PHY_TX_OFFS) :
+					       (RESULT_PHY_RX_OFFS))))
+					| pup_win_length <<
+					((mode == CENTRAL_TX) ?
+					 (RESULT_PHY_TX_OFFS) :
+					 (RESULT_PHY_RX_OFFS));
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      bus_id, DDR_PHY_DATA,
+					      RESULT_PHY_REG +
+					      effective_cs, reg));
+
+				/* offset per CS is calculated earlier */
+				CHECK_STATUS(
+					ddr3_tip_bus_write(dev_num,
+							   ACCESS_TYPE_UNICAST,
+							   if_id,
+							   ACCESS_TYPE_UNICAST,
+							   bus_id,
+							   DDR_PHY_DATA,
+							   reg_phy_off,
+							   centralization_result
+							   [if_id]
+							   [bus_id]));
+			} else {
+				is_if_fail = 1;
+			}
+		}
+
+		if (is_if_fail == 1)
+			flow_result[if_id] = TEST_FAILED;
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		/* restore cs enable value */
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST,
+					       if_id, DUAL_DUNIT_CFG_REG,
+					       cs_enable_reg_val[if_id],
+					       MASK_ALL_BITS));
+	}
+
+	return is_if_fail;
+}
+
+/*
+ * Centralization Flow
+ */
+int ddr3_tip_special_rx(u32 dev_num)
+{
+	enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM];
+	u32 if_id, pup_id, pattern_id, bit_id;
+	u8 cur_start_win[BUS_WIDTH_IN_BITS];
+	u8 cur_end_win[BUS_WIDTH_IN_BITS];
+	enum hws_training_result result_type = RESULT_PER_BIT;
+	enum hws_dir direction;
+	enum hws_search_dir search_dir_id;
+	u32 *result[HWS_SEARCH_DIR_LIMIT];
+	u32 max_win_size;
+	u8 cur_end_win_min, cur_start_win_max;
+	u32 cs_enable_reg_val[MAX_INTERFACE_NUM];
+	u32 temp = 0;
+	int pad_num = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if ((ddr3_tip_special_rx_run_once_flag & (1 << effective_cs)) == (1 << effective_cs))
+		return MV_OK;
+
+	ddr3_tip_special_rx_run_once_flag |= (1 << effective_cs);
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* save current cs enable reg val */
+		CHECK_STATUS(ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, DUAL_DUNIT_CFG_REG,
+					      cs_enable_reg_val,
+					      MASK_ALL_BITS));
+		/* enable single cs */
+		CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST,
+					       if_id, DUAL_DUNIT_CFG_REG,
+					       (1 << 3), (1 << 3)));
+	}
+
+	max_win_size = MAX_WINDOW_SIZE_RX;
+	direction = OPER_READ;
+	pattern_id = PATTERN_FULL_SSO1;
+
+	/* start flow */
+	ddr3_tip_ip_training_wrapper(dev_num, ACCESS_TYPE_MULTICAST,
+				     PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST,
+				     PARAM_NOT_CARE, result_type,
+				     HWS_CONTROL_ELEMENT_ADLL,
+				     PARAM_NOT_CARE, direction,
+				     tm->if_act_mask, 0x0,
+				     max_win_size - 1, max_win_size - 1,
+				     pattern_id, EDGE_FPF, CS_SINGLE,
+				     PARAM_NOT_CARE, training_result);
+
+	for (if_id = start_if; if_id <= end_if; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup_id = 0;
+		     pup_id <= octets_per_if_num; pup_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup_id);
+
+			for (search_dir_id = HWS_LOW2HIGH;
+			     search_dir_id <= HWS_HIGH2LOW;
+			     search_dir_id++) {
+				CHECK_STATUS(ddr3_tip_read_training_result
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, pup_id,
+					      ALL_BITS_PER_PUP, search_dir_id,
+					      direction, result_type,
+					      TRAINING_LOAD_OPERATION_UNLOAD,
+					      CS_SINGLE, &result[search_dir_id],
+					      1, 0, 0));
+				DEBUG_CENTRALIZATION_ENGINE(DEBUG_LEVEL_INFO,
+							    ("Special: pat %d IF %d pup %d Regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+							     pattern_id, if_id,
+							     pup_id,
+							     result
+							     [search_dir_id][0],
+							     result
+							     [search_dir_id][1],
+							     result
+							     [search_dir_id][2],
+							     result
+							     [search_dir_id][3],
+							     result
+							     [search_dir_id][4],
+							     result
+							     [search_dir_id][5],
+							     result
+							     [search_dir_id][6],
+							     result
+							     [search_dir_id]
+							     [7]));
+			}
+
+			for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS; bit_id++) {
+				/*
+				 * check if this code is valid for 2 edge,
+				 * probably not :(
+				 */
+				cur_start_win[bit_id] =
+					GET_TAP_RESULT(result[HWS_LOW2HIGH]
+						       [bit_id], EDGE_1);
+				cur_end_win[bit_id] =
+					GET_TAP_RESULT(result[HWS_HIGH2LOW]
+						       [bit_id], EDGE_1);
+			}
+			if (!((ddr3_tip_is_pup_lock
+			       (result[HWS_LOW2HIGH], result_type)) &&
+			      (ddr3_tip_is_pup_lock
+			       (result[HWS_HIGH2LOW], result_type)))) {
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_ERROR,
+					("Special: Pup lock fail, pat %d IF %d pup %d\n",
+					 pattern_id, if_id, pup_id));
+				return MV_FAIL;
+			}
+
+			cur_end_win_min =
+				ddr3_tip_get_buf_min(cur_end_win);
+			cur_start_win_max =
+				ddr3_tip_get_buf_max(cur_start_win);
+
+			if (cur_start_win_max <= 1) {	/* Align left */
+				for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS;
+				     bit_id++) {
+					pad_num =
+						dq_map_table[bit_id +
+							     pup_id *
+							     BUS_WIDTH_IN_BITS +
+							     if_id *
+							     BUS_WIDTH_IN_BITS *
+							     MAX_BUS_NUM];
+					CHECK_STATUS(ddr3_tip_bus_read
+						     (dev_num, if_id,
+						      ACCESS_TYPE_UNICAST,
+						      pup_id, DDR_PHY_DATA,
+						      PBS_RX_PHY_REG(effective_cs, pad_num),
+						      &temp));
+					temp = (temp + 0xa > 31) ?
+						(31) : (temp + 0xa);
+					CHECK_STATUS(ddr3_tip_bus_write
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      ACCESS_TYPE_UNICAST,
+						      pup_id, DDR_PHY_DATA,
+						      PBS_RX_PHY_REG(effective_cs, pad_num),
+						      temp));
+				}
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("Special: PBS:: I/F# %d , Bus# %d fix align to the Left\n",
+					 if_id, pup_id));
+			}
+
+			if (cur_end_win_min > 30) { /* Align right */
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, pup_id,
+					      DDR_PHY_DATA,
+					      PBS_RX_PHY_REG(effective_cs, 4),
+					      &temp));
+				temp += 0xa;
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      pup_id, DDR_PHY_DATA,
+					      PBS_RX_PHY_REG(effective_cs, 4),
+					      temp));
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, pup_id,
+					      DDR_PHY_DATA,
+					      PBS_RX_PHY_REG(effective_cs, 5),
+					      &temp));
+				temp += 0xa;
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      pup_id, DDR_PHY_DATA,
+					      PBS_RX_PHY_REG(effective_cs, 5),
+					      temp));
+				DEBUG_CENTRALIZATION_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("Special: PBS:: I/F# %d , Bus# %d fix align to the right\n",
+					 if_id, pup_id));
+			}
+
+			vref_window_size[if_id][pup_id] =
+				cur_end_win_min -
+				cur_start_win_max + 1;
+			DEBUG_CENTRALIZATION_ENGINE(
+				DEBUG_LEVEL_INFO,
+				("Special: Winsize I/F# %d , Bus# %d is %d\n",
+				 if_id, pup_id, vref_window_size
+				 [if_id][pup_id]));
+		}		/* pup */
+	}			/* end of interface */
+
+	return MV_OK;
+}
+
+/*
+ * Print Centralization Result
+ */
+int ddr3_tip_print_centralization_result(u32 dev_num)
+{
+	u32 if_id = 0, bus_id = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	dev_num = dev_num;
+
+	printf("Centralization Results\n");
+	printf("I/F0 Result[0 - success 1-fail 2 - state_2 3 - state_3] ...\n");
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0; bus_id < octets_per_if_num;
+		     bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			printf("%d ,\n", centralization_state[if_id][bus_id]);
+		}
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_db.c b/drivers/ddr/marvell/a38x/ddr3_training_db.c
new file mode 100644
index 0000000..111a858
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_db.c
@@ -0,0 +1,871 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr_ml_wrapper.h"
+
+#include "ddr3_training_ip_flow.h"
+#include "mv_ddr_topology.h"
+#include "mv_ddr_training_db.h"
+#include "ddr3_training_ip_db.h"
+
+/* Device attributes structures */
+enum mv_ddr_dev_attribute ddr_dev_attributes[MV_ATTR_LAST];
+int ddr_dev_attr_init_done = 0;
+
+static inline u32 pattern_table_get_killer_word16(u8 dqs, u8 index);
+static inline u32 pattern_table_get_sso_word(u8 sso, u8 index);
+static inline u32 pattern_table_get_vref_word(u8 index);
+static inline u32 pattern_table_get_vref_word16(u8 index);
+static inline u32 pattern_table_get_sso_full_xtalk_word(u8 bit, u8 index);
+static inline u32 pattern_table_get_sso_full_xtalk_word16(u8 bit, u8 index);
+static inline u32 pattern_table_get_sso_xtalk_free_word(u8 bit, u8 index);
+static inline u32 pattern_table_get_sso_xtalk_free_word16(u8 bit, u8 index);
+static inline u32 pattern_table_get_isi_word(u8 index);
+static inline u32 pattern_table_get_isi_word16(u8 index);
+
+/* List of allowed frequency listed in order of enum mv_ddr_freq */
+static unsigned int freq_val[MV_DDR_FREQ_LAST] = {
+	0,			/*MV_DDR_FREQ_LOW_FREQ */
+	400,			/*MV_DDR_FREQ_400, */
+	533,			/*MV_DDR_FREQ_533, */
+	666,			/*MV_DDR_FREQ_667, */
+	800,			/*MV_DDR_FREQ_800, */
+	933,			/*MV_DDR_FREQ_933, */
+	1066,			/*MV_DDR_FREQ_1066, */
+	311,			/*MV_DDR_FREQ_311, */
+	333,			/*MV_DDR_FREQ_333, */
+	467,			/*MV_DDR_FREQ_467, */
+	850,			/*MV_DDR_FREQ_850, */
+	600,			/*MV_DDR_FREQ_600 */
+	300,			/*MV_DDR_FREQ_300 */
+	900,			/*MV_DDR_FREQ_900 */
+	360,			/*MV_DDR_FREQ_360 */
+	1000			/*MV_DDR_FREQ_1000 */
+};
+
+unsigned int *mv_ddr_freq_tbl_get(void)
+{
+	return &freq_val[0];
+}
+
+u32 mv_ddr_freq_get(enum mv_ddr_freq freq)
+{
+	return freq_val[freq];
+}
+
+/* cas latency values per frequency for each speed bin index */
+static struct mv_ddr_cl_val_per_freq cl_table[] = {
+	/*
+	 * 400M   667M     933M   311M     467M  600M    360
+	 * 100M    533M    800M    1066M   333M    850M      900
+	 * 1000 (the order is 100, 400, 533 etc.)
+	 */
+	/* DDR3-800D */
+	{ {6, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 5, 0, 5, 0} },
+	/* DDR3-800E */
+	{ {6, 6, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 6, 0, 6, 0} },
+	/* DDR3-1066E */
+	{ {6, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 0, 5, 0, 5, 0} },
+	/* DDR3-1066F */
+	{ {6, 6, 7, 0, 0, 0, 0, 6, 6, 7, 0, 0, 6, 0, 6, 0} },
+	/* DDR3-1066G */
+	{ {6, 6, 8, 0, 0, 0, 0, 6, 6, 8, 0, 0, 6, 0, 6, 0} },
+	/* DDR3-1333F* */
+	{ {6, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1333G */
+	{ {6, 5, 7, 8, 0, 0, 0, 5, 5, 7, 0, 8, 5, 0, 5, 0} },
+	/* DDR3-1333H */
+	{ {6, 6, 8, 9, 0, 0, 0, 6, 6, 8, 0, 9, 6, 0, 6, 0} },
+	/* DDR3-1333J* */
+	{ {6, 6, 8, 10, 0, 0, 0, 6, 6, 8, 0, 10, 6, 0, 6,  0}
+	 /* DDR3-1600G* */},
+	{ {6, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1600H */
+	{ {6, 5, 6, 8, 9, 0, 0, 5, 5, 6, 0, 8, 5, 0, 5, 0} },
+	/* DDR3-1600J */
+	{ {6, 5, 7, 9, 10, 0, 0, 5, 5, 7, 0, 9, 5, 0, 5, 0} },
+	/* DDR3-1600K */
+	{ {6, 6, 8, 10, 11, 0, 0, 6, 6, 8, 0, 10, 6, 0, 6, 0 } },
+	/* DDR3-1866J* */
+	{ {6, 5, 6, 8, 9, 11, 0, 5, 5, 6, 11, 8, 5, 0, 5, 0} },
+	/* DDR3-1866K */
+	{ {6, 5, 7, 8, 10, 11, 0, 5, 5, 7, 11, 8, 5, 11, 5, 11} },
+	/* DDR3-1866L */
+	{ {6, 6, 7, 9, 11, 12, 0, 6, 6, 7, 12, 9, 6, 12, 6, 12} },
+	/* DDR3-1866M* */
+	{ {6, 6, 8, 10, 11, 13, 0, 6, 6, 8, 13, 10, 6, 13, 6, 13} },
+	/* DDR3-2133K* */
+	{ {6, 5, 6, 7, 9, 10, 11, 5, 5, 6, 10, 7, 5, 11, 5, 11} },
+	/* DDR3-2133L */
+	{ {6, 5, 6, 8, 9, 11, 12, 5, 5, 6, 11, 8, 5, 12, 5, 12} },
+	/* DDR3-2133M */
+	{ {6, 5, 7, 9, 10, 12, 13, 5, 5, 7, 12, 9, 5, 13, 5, 13} },
+	/* DDR3-2133N* */
+	{ {6, 6, 7, 9, 11, 13, 14, 6, 6, 7, 13, 9, 6, 14,  6, 14} },
+	/* DDR3-1333H-ext */
+	{ {6, 6, 7, 9, 0, 0, 0, 6, 6, 7, 0, 9, 6, 0, 6, 0} },
+	/* DDR3-1600K-ext */
+	{ {6, 6, 7, 9, 11, 0, 0, 6, 6, 7, 0, 9, 6, 0, 6, 0} },
+	/* DDR3-1866M-ext */
+	{ {6, 6, 7, 9, 11, 13, 0, 6, 6, 7, 13, 9, 6, 13, 6, 13} },
+};
+
+u32 mv_ddr_cl_val_get(u32 index, u32 freq)
+{
+	return cl_table[index].cl_val[freq];
+}
+
+/* cas write latency values per frequency for each speed bin index */
+static struct mv_ddr_cl_val_per_freq cwl_table[] = {
+	/*
+	 * 400M   667M     933M   311M     467M  600M    360
+	 * 100M    533M    800M    1066M   333M    850M      900
+	 * (the order is 100, 400, 533 etc.)
+	 */
+	/* DDR3-800D  */
+	{ {5, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 5, 0, 5, 0} },
+	/* DDR3-800E  */
+	{ {5, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 5, 0, 5, 0} },
+	/* DDR3-1066E  */
+	{ {5, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1066F  */
+	{ {5, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1066G  */
+	{ {5, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1333F*  */
+	{ {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1333G  */
+	{ {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1333H  */
+	{ {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1333J*  */
+	{ {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1600G*  */
+	{ {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1600H  */
+	{ {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1600J  */
+	{ {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1600K  */
+	{ {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1866J*  */
+	{ {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 0, 5, 0} },
+	/* DDR3-1866K  */
+	{ {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 0, 5, 0} },
+	/* DDR3-1866L  */
+	{ {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 9, 5, 9} },
+	/* DDR3-1866M*   */
+	{ {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 9, 5, 9} },
+	/* DDR3-2133K*  */
+	{ {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} },
+	/* DDR3-2133L  */
+	{ {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} },
+	/* DDR3-2133M  */
+	{ {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} },
+	/* DDR3-2133N*  */
+	{ {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} },
+	/* DDR3-1333H-ext  */
+	{ {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1600K-ext  */
+	{ {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} },
+	/* DDR3-1866M-ext  */
+	{ {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 9, 5, 9} },
+};
+
+u32 mv_ddr_cwl_val_get(u32 index, u32 freq)
+{
+	return cwl_table[index].cl_val[freq];
+}
+
+u8 twr_mask_table[] = {
+	10,
+	10,
+	10,
+	10,
+	10,
+	1,			/* 5 */
+	2,			/* 6 */
+	3,			/* 7 */
+	4,			/* 8 */
+	10,
+	5,			/* 10 */
+	10,
+	6,			/* 12 */
+	10,
+	7,			/* 14 */
+	10,
+	0			/* 16 */
+};
+
+u8 cl_mask_table[] = {
+	0,
+	0,
+	0,
+	0,
+	0,
+	0x2,
+	0x4,
+	0x6,
+	0x8,
+	0xa,
+	0xc,
+	0xe,
+	0x1,
+	0x3,
+	0x5,
+	0x5
+};
+
+u8 cwl_mask_table[] = {
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0x1,
+	0x2,
+	0x3,
+	0x4,
+	0x5,
+	0x6,
+	0x7,
+	0x8,
+	0x9,
+	0x9
+};
+
+/* RFC values (in ns) */
+static unsigned int rfc_table[] = {
+	90,	/* 512M */
+	110,	/* 1G */
+	160,	/* 2G */
+	260,	/* 4G */
+	350,	/* 8G */
+	0,	/* TODO: placeholder for 16-Mbit dev width */
+	0,	/* TODO: placeholder for 32-Mbit dev width */
+	0,	/* TODO: placeholder for 12-Mbit dev width */
+	0	/* TODO: placeholder for 24-Mbit dev width */
+};
+
+u32 mv_ddr_rfc_get(u32 mem)
+{
+	return rfc_table[mem];
+}
+
+u32 speed_bin_table_t_rc[] = {
+	50000,
+	52500,
+	48750,
+	50625,
+	52500,
+	46500,
+	48000,
+	49500,
+	51000,
+	45000,
+	46250,
+	47500,
+	48750,
+	44700,
+	45770,
+	46840,
+	47910,
+	43285,
+	44220,
+	45155,
+	46090
+};
+
+u32 speed_bin_table_t_rcd_t_rp[] = {
+	12500,
+	15000,
+	11250,
+	13125,
+	15000,
+	10500,
+	12000,
+	13500,
+	15000,
+	10000,
+	11250,
+	12500,
+	13750,
+	10700,
+	11770,
+	12840,
+	13910,
+	10285,
+	11220,
+	12155,
+	13090,
+};
+
+enum {
+	PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR = 0,
+	PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM
+};
+
+static u8 pattern_killer_pattern_table_map[KILLER_PATTERN_LENGTH * 2][2] = {
+	/*Aggressor / Victim */
+	{1, 0},
+	{0, 0},
+	{1, 0},
+	{1, 1},
+	{0, 1},
+	{0, 1},
+	{1, 0},
+	{0, 1},
+	{1, 0},
+	{0, 1},
+	{1, 0},
+	{1, 0},
+	{0, 1},
+	{1, 0},
+	{0, 1},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{1, 0},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{0, 0},
+	{0, 0},
+	{0, 1},
+	{0, 1},
+	{1, 1},
+	{0, 0},
+	{0, 0},
+	{1, 1},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{1, 1},
+	{0, 0},
+	{0, 0},
+	{1, 1},
+	{0, 0},
+	{1, 1},
+	{0, 1},
+	{0, 0},
+	{0, 1},
+	{0, 1},
+	{0, 0},
+	{1, 1},
+	{1, 1},
+	{1, 0},
+	{1, 0},
+	{1, 1},
+	{1, 1},
+	{1, 1},
+	{1, 1},
+	{1, 1},
+	{1, 1},
+	{1, 1}
+};
+
+static u8 pattern_vref_pattern_table_map[] = {
+	/* 1 means 0xffffffff, 0 is 0x0 */
+	0xb8,
+	0x52,
+	0x55,
+	0x8a,
+	0x33,
+	0xa6,
+	0x6d,
+	0xfe
+};
+
+static struct mv_ddr_page_element page_tbl[] = {
+	/* 8-bit, 16-bit page size */
+	{MV_DDR_PAGE_SIZE_1K, MV_DDR_PAGE_SIZE_2K}, /* 512M */
+	{MV_DDR_PAGE_SIZE_1K, MV_DDR_PAGE_SIZE_2K}, /* 1G */
+	{MV_DDR_PAGE_SIZE_1K, MV_DDR_PAGE_SIZE_2K}, /* 2G */
+	{MV_DDR_PAGE_SIZE_1K, MV_DDR_PAGE_SIZE_2K}, /* 4G */
+	{MV_DDR_PAGE_SIZE_2K, MV_DDR_PAGE_SIZE_2K}, /* 8G */
+	{0, 0}, /* TODO: placeholder for 16-Mbit die capacity */
+	{0, 0}, /* TODO: placeholder for 32-Mbit die capacity */
+	{0, 0}, /* TODO: placeholder for 12-Mbit die capacity */
+	{0, 0}  /* TODO: placeholder for 24-Mbit die capacity */
+};
+
+u32 mv_ddr_page_size_get(enum mv_ddr_dev_width bus_width, enum mv_ddr_die_capacity mem_size)
+{
+	if (bus_width == MV_DDR_DEV_WIDTH_8BIT)
+		return page_tbl[mem_size].page_size_8bit;
+	else
+		return page_tbl[mem_size].page_size_16bit;
+}
+
+/* Return speed Bin value for selected index and t* element */
+unsigned int mv_ddr_speed_bin_timing_get(enum mv_ddr_speed_bin index, enum mv_ddr_speed_bin_timing element)
+{
+	u32 result = 0;
+
+	switch (element) {
+	case SPEED_BIN_TRCD:
+	case SPEED_BIN_TRP:
+		result = speed_bin_table_t_rcd_t_rp[index];
+		break;
+	case SPEED_BIN_TRAS:
+		if (index < SPEED_BIN_DDR_1066G)
+			result = 37500;
+		else if (index < SPEED_BIN_DDR_1333J)
+			result = 36000;
+		else if (index < SPEED_BIN_DDR_1600K)
+			result = 35000;
+		else if (index < SPEED_BIN_DDR_1866M)
+			result = 34000;
+		else
+			result = 33000;
+		break;
+	case SPEED_BIN_TRC:
+		result = speed_bin_table_t_rc[index];
+		break;
+	case SPEED_BIN_TRRD1K:
+		if (index <= SPEED_BIN_DDR_800E)
+			result = 10000;
+		else if (index <= SPEED_BIN_DDR_1066G)
+			result = 7500;
+		else if (index <= SPEED_BIN_DDR_1600K)
+			result = 6000;
+		else
+			result = 5000;
+		break;
+	case SPEED_BIN_TRRD2K:
+		if (index <= SPEED_BIN_DDR_1066G)
+			result = 10000;
+		else if (index <= SPEED_BIN_DDR_1600K)
+			result = 7500;
+		else
+			result = 6000;
+		break;
+	case SPEED_BIN_TPD:
+		if (index < SPEED_BIN_DDR_800E)
+			result = 7500;
+		else if (index < SPEED_BIN_DDR_1333J)
+			result = 5625;
+		else
+			result = 5000;
+		break;
+	case SPEED_BIN_TFAW1K:
+		if (index <= SPEED_BIN_DDR_800E)
+			result = 40000;
+		else if (index <= SPEED_BIN_DDR_1066G)
+			result = 37500;
+		else if (index <= SPEED_BIN_DDR_1600K)
+			result = 30000;
+		else if (index <= SPEED_BIN_DDR_1866M)
+			result = 27000;
+		else
+			result = 25000;
+		break;
+	case SPEED_BIN_TFAW2K:
+		if (index <= SPEED_BIN_DDR_1066G)
+			result = 50000;
+		else if (index <= SPEED_BIN_DDR_1333J)
+			result = 45000;
+		else if (index <= SPEED_BIN_DDR_1600K)
+			result = 40000;
+		else
+			result = 35000;
+		break;
+	case SPEED_BIN_TWTR:
+		result = 7500;
+		break;
+	case SPEED_BIN_TRTP:
+		result = 7500;
+		break;
+	case SPEED_BIN_TWR:
+		result = 15000;
+		break;
+	case SPEED_BIN_TMOD:
+		result = 15000;
+		break;
+	case SPEED_BIN_TXPDLL:
+		result = 24000;
+		break;
+	case SPEED_BIN_TXSDLL:
+		result = 512;
+		break;
+	default:
+		break;
+	}
+
+	return result;
+}
+
+static inline u32 pattern_table_get_killer_word(u8 dqs, u8 index)
+{
+	u8 i, byte = 0;
+	u8 role;
+
+	for (i = 0; i < 8; i++) {
+		role = (i == dqs) ?
+			(PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR) :
+			(PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM);
+		byte |= pattern_killer_pattern_table_map[index][role] << i;
+	}
+
+	return byte | (byte << 8) | (byte << 16) | (byte << 24);
+}
+
+static inline u32 pattern_table_get_killer_word16(u8 dqs, u8 index)
+{
+	u8 i, byte0 = 0, byte1 = 0;
+	u8 role;
+
+	for (i = 0; i < 8; i++) {
+		role = (i == dqs) ?
+			(PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR) :
+			(PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM);
+		byte0 |= pattern_killer_pattern_table_map[index * 2][role] << i;
+		byte1 |= pattern_killer_pattern_table_map[index * 2 + 1][role] << i;
+	}
+
+	return byte0 | (byte0 << 8) | (byte1 << 16) | (byte1 << 24);
+}
+
+static inline u32 pattern_table_get_sso_word(u8 sso, u8 index)
+{
+	u8 step = sso + 1;
+
+	if (0 == ((index / step) & 1))
+		return 0x0;
+	else
+		return 0xffffffff;
+}
+
+static inline u32 pattern_table_get_sso_full_xtalk_word(u8 bit, u8 index)
+{
+	u8 byte = (1 << bit);
+
+	if ((index & 1) == 1)
+		byte = ~byte;
+
+	return byte | (byte << 8) | (byte << 16) | (byte << 24);
+
+}
+
+static inline u32 pattern_table_get_sso_xtalk_free_word(u8 bit, u8 index)
+{
+	u8 byte = (1 << bit);
+
+	if ((index & 1) == 1)
+		byte = 0;
+
+	return byte | (byte << 8) | (byte << 16) | (byte << 24);
+}
+
+static inline u32 pattern_table_get_isi_word(u8 index)
+{
+	u8 i0 = index % 32;
+	u8 i1 = index % 8;
+	u32 word;
+
+	if (i0 > 15)
+		word = ((i1 == 5) | (i1 == 7)) ? 0xffffffff : 0x0;
+	else
+		word = (i1 == 6) ? 0xffffffff : 0x0;
+
+	word = ((i0 % 16) > 7) ? ~word : word;
+
+	return word;
+}
+
+static inline u32 pattern_table_get_sso_full_xtalk_word16(u8 bit, u8 index)
+{
+	u8 byte = (1 << bit);
+
+	if ((index & 1) == 1)
+		byte = ~byte;
+
+	return byte | (byte << 8) | ((~byte) << 16) | ((~byte) << 24);
+}
+
+static inline u32 pattern_table_get_sso_xtalk_free_word16(u8 bit, u8 index)
+{
+	u8 byte = (1 << bit);
+
+	if ((index & 1) == 0)
+		return (byte << 16) | (byte << 24);
+	else
+		return byte | (byte << 8);
+}
+
+static inline u32 pattern_table_get_isi_word16(u8 index)
+{
+	u8 i0 = index % 16;
+	u8 i1 = index % 4;
+	u32 word;
+
+	if (i0 > 7)
+		word = (i1 > 1) ? 0x0000ffff : 0x0;
+	else
+		word = (i1 == 3) ? 0xffff0000 : 0x0;
+
+	word = ((i0 % 8) > 3) ? ~word : word;
+
+	return word;
+}
+
+static inline u32 pattern_table_get_vref_word(u8 index)
+{
+	if (0 == ((pattern_vref_pattern_table_map[index / 8] >>
+		   (index % 8)) & 1))
+		return 0x0;
+	else
+		return 0xffffffff;
+}
+
+static inline u32 pattern_table_get_vref_word16(u8 index)
+{
+	if (0 == pattern_killer_pattern_table_map
+	    [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2] &&
+	    0 == pattern_killer_pattern_table_map
+	    [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2 + 1])
+		return 0x00000000;
+	else if (1 == pattern_killer_pattern_table_map
+		 [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2] &&
+		 0 == pattern_killer_pattern_table_map
+		 [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2 + 1])
+		return 0xffff0000;
+	else if (0 == pattern_killer_pattern_table_map
+		 [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2] &&
+		 1 == pattern_killer_pattern_table_map
+		 [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2 + 1])
+		return 0x0000ffff;
+	else
+		return 0xffffffff;
+}
+
+static inline u32 pattern_table_get_static_pbs_word(u8 index)
+{
+	u16 temp;
+
+	temp = ((0x00ff << (index / 3)) & 0xff00) >> 8;
+
+	return temp | (temp << 8) | (temp << 16) | (temp << 24);
+}
+
+u32 pattern_table_get_word(u32 dev_num, enum hws_pattern type, u8 index)
+{
+	u32 pattern = 0;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) == 0) {
+		/* 32/64-bit patterns */
+		switch (type) {
+		case PATTERN_PBS1:
+		case PATTERN_PBS2:
+			if (index == 0 || index == 2 || index == 5 ||
+			    index == 7)
+				pattern = PATTERN_55;
+			else
+				pattern = PATTERN_AA;
+			break;
+		case PATTERN_PBS3:
+			if (0 == (index & 1))
+				pattern = PATTERN_55;
+			else
+				pattern = PATTERN_AA;
+			break;
+		case PATTERN_RL:
+			if (index < 6)
+				pattern = PATTERN_00;
+			else
+				pattern = PATTERN_80;
+			break;
+		case PATTERN_STATIC_PBS:
+			pattern = pattern_table_get_static_pbs_word(index);
+			break;
+		case PATTERN_KILLER_DQ0:
+		case PATTERN_KILLER_DQ1:
+		case PATTERN_KILLER_DQ2:
+		case PATTERN_KILLER_DQ3:
+		case PATTERN_KILLER_DQ4:
+		case PATTERN_KILLER_DQ5:
+		case PATTERN_KILLER_DQ6:
+		case PATTERN_KILLER_DQ7:
+			pattern = pattern_table_get_killer_word(
+				(u8)(type - PATTERN_KILLER_DQ0), index);
+			break;
+		case PATTERN_RL2:
+			if (index < 6)
+				pattern = PATTERN_00;
+			else
+				pattern = PATTERN_01;
+			break;
+		case PATTERN_TEST:
+			if (index > 1 && index < 6)
+				pattern = PATTERN_00;
+			else
+				pattern = PATTERN_FF;
+			break;
+		case PATTERN_FULL_SSO0:
+		case PATTERN_FULL_SSO1:
+		case PATTERN_FULL_SSO2:
+		case PATTERN_FULL_SSO3:
+			pattern = pattern_table_get_sso_word(
+				(u8)(type - PATTERN_FULL_SSO0), index);
+			break;
+		case PATTERN_VREF:
+			pattern = pattern_table_get_vref_word(index);
+			break;
+		case PATTERN_SSO_FULL_XTALK_DQ0:
+		case PATTERN_SSO_FULL_XTALK_DQ1:
+		case PATTERN_SSO_FULL_XTALK_DQ2:
+		case PATTERN_SSO_FULL_XTALK_DQ3:
+		case PATTERN_SSO_FULL_XTALK_DQ4:
+		case PATTERN_SSO_FULL_XTALK_DQ5:
+		case PATTERN_SSO_FULL_XTALK_DQ6:
+		case PATTERN_SSO_FULL_XTALK_DQ7:
+			pattern = pattern_table_get_sso_full_xtalk_word(
+				(u8)(type - PATTERN_SSO_FULL_XTALK_DQ0), index);
+			break;
+		case PATTERN_SSO_XTALK_FREE_DQ0:
+		case PATTERN_SSO_XTALK_FREE_DQ1:
+		case PATTERN_SSO_XTALK_FREE_DQ2:
+		case PATTERN_SSO_XTALK_FREE_DQ3:
+		case PATTERN_SSO_XTALK_FREE_DQ4:
+		case PATTERN_SSO_XTALK_FREE_DQ5:
+		case PATTERN_SSO_XTALK_FREE_DQ6:
+		case PATTERN_SSO_XTALK_FREE_DQ7:
+			pattern = pattern_table_get_sso_xtalk_free_word(
+				(u8)(type - PATTERN_SSO_XTALK_FREE_DQ0), index);
+			break;
+		case PATTERN_ISI_XTALK_FREE:
+			pattern = pattern_table_get_isi_word(index);
+			break;
+		default:
+			printf("error: %s: unsupported pattern type [%d] found\n",
+			       __func__, (int)type);
+			pattern = 0;
+			break;
+		}
+	} else {
+		/* 16bit patterns */
+		switch (type) {
+		case PATTERN_PBS1:
+		case PATTERN_PBS2:
+		case PATTERN_PBS3:
+			pattern = PATTERN_55AA;
+			break;
+		case PATTERN_RL:
+			if (index < 3)
+				pattern = PATTERN_00;
+			else
+				pattern = PATTERN_80;
+			break;
+		case PATTERN_STATIC_PBS:
+			pattern = PATTERN_00FF;
+			break;
+		case PATTERN_KILLER_DQ0:
+		case PATTERN_KILLER_DQ1:
+		case PATTERN_KILLER_DQ2:
+		case PATTERN_KILLER_DQ3:
+		case PATTERN_KILLER_DQ4:
+		case PATTERN_KILLER_DQ5:
+		case PATTERN_KILLER_DQ6:
+		case PATTERN_KILLER_DQ7:
+			pattern = pattern_table_get_killer_word16(
+				(u8)(type - PATTERN_KILLER_DQ0), index);
+			break;
+		case PATTERN_RL2:
+			if (index < 3)
+				pattern = PATTERN_00;
+			else
+				pattern = PATTERN_01;
+			break;
+		case PATTERN_TEST:
+			if ((index == 0) || (index == 3))
+				pattern = 0x00000000;
+			else
+				pattern = 0xFFFFFFFF;
+			break;
+		case PATTERN_FULL_SSO0:
+			pattern = 0x0000ffff;
+			break;
+		case PATTERN_FULL_SSO1:
+		case PATTERN_FULL_SSO2:
+		case PATTERN_FULL_SSO3:
+			pattern = pattern_table_get_sso_word(
+				(u8)(type - PATTERN_FULL_SSO1), index);
+			break;
+		case PATTERN_VREF:
+			pattern = pattern_table_get_vref_word16(index);
+			break;
+		case PATTERN_SSO_FULL_XTALK_DQ0:
+		case PATTERN_SSO_FULL_XTALK_DQ1:
+		case PATTERN_SSO_FULL_XTALK_DQ2:
+		case PATTERN_SSO_FULL_XTALK_DQ3:
+		case PATTERN_SSO_FULL_XTALK_DQ4:
+		case PATTERN_SSO_FULL_XTALK_DQ5:
+		case PATTERN_SSO_FULL_XTALK_DQ6:
+		case PATTERN_SSO_FULL_XTALK_DQ7:
+			pattern = pattern_table_get_sso_full_xtalk_word16(
+				(u8)(type - PATTERN_SSO_FULL_XTALK_DQ0), index);
+			break;
+		case PATTERN_SSO_XTALK_FREE_DQ0:
+		case PATTERN_SSO_XTALK_FREE_DQ1:
+		case PATTERN_SSO_XTALK_FREE_DQ2:
+		case PATTERN_SSO_XTALK_FREE_DQ3:
+		case PATTERN_SSO_XTALK_FREE_DQ4:
+		case PATTERN_SSO_XTALK_FREE_DQ5:
+		case PATTERN_SSO_XTALK_FREE_DQ6:
+		case PATTERN_SSO_XTALK_FREE_DQ7:
+			pattern = pattern_table_get_sso_xtalk_free_word16(
+				(u8)(type - PATTERN_SSO_XTALK_FREE_DQ0), index);
+			break;
+		case PATTERN_ISI_XTALK_FREE:
+			pattern = pattern_table_get_isi_word16(index);
+			break;
+		default:
+			printf("error: %s: unsupported pattern type [%d] found\n",
+			       __func__, (int)type);
+			pattern = 0;
+			break;
+		}
+	}
+
+	return pattern;
+}
+
+/* Device attribute functions */
+void ddr3_tip_dev_attr_init(u32 dev_num)
+{
+	u32 attr_id;
+
+	for (attr_id = 0; attr_id < MV_ATTR_LAST; attr_id++)
+		ddr_dev_attributes[attr_id] = 0xFF;
+
+	ddr_dev_attr_init_done = 1;
+}
+
+u32 ddr3_tip_dev_attr_get(u32 dev_num, enum mv_ddr_dev_attribute attr_id)
+{
+	if (ddr_dev_attr_init_done == 0)
+		ddr3_tip_dev_attr_init(dev_num);
+
+	return ddr_dev_attributes[attr_id];
+}
+
+void ddr3_tip_dev_attr_set(u32 dev_num, enum mv_ddr_dev_attribute attr_id, u32 value)
+{
+	if (ddr_dev_attr_init_done == 0)
+		ddr3_tip_dev_attr_init(dev_num);
+
+	ddr_dev_attributes[attr_id] = value;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c
new file mode 100644
index 0000000..db0f8ad
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c
@@ -0,0 +1,676 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_regs.h"
+
+#define VREF_INITIAL_STEP		3
+#define VREF_SECOND_STEP		1
+#define VREF_MAX_INDEX			7
+#define MAX_VALUE			(1024 - 1)
+#define MIN_VALUE			(-MAX_VALUE)
+#define GET_RD_SAMPLE_DELAY(data, cs)	((data >> rd_sample_mask[cs]) & 0xf)
+
+u32 ca_delay;
+int ddr3_tip_centr_skip_min_win_check = 0;
+u8 current_vref[MAX_BUS_NUM][MAX_INTERFACE_NUM];
+u8 last_vref[MAX_BUS_NUM][MAX_INTERFACE_NUM];
+u16 current_valid_window[MAX_BUS_NUM][MAX_INTERFACE_NUM];
+u16 last_valid_window[MAX_BUS_NUM][MAX_INTERFACE_NUM];
+u8 lim_vref[MAX_BUS_NUM][MAX_INTERFACE_NUM];
+u8 interface_state[MAX_INTERFACE_NUM];
+u8 vref_window_size[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 vref_window_size_th = 12;
+
+static u8 pup_st[MAX_BUS_NUM][MAX_INTERFACE_NUM];
+
+static u32 rd_sample_mask[] = {
+	0,
+	8,
+	16,
+	24
+};
+
+#define	VREF_STEP_1		0
+#define	VREF_STEP_2		1
+#define	VREF_CONVERGE		2
+
+/*
+ * ODT additional timing
+ */
+int ddr3_tip_write_additional_odt_setting(u32 dev_num, u32 if_id)
+{
+	u32 cs_num = 0, max_read_sample = 0, min_read_sample = 0x1f;
+	u32 data_read[MAX_INTERFACE_NUM] = { 0 };
+	u32 read_sample[MAX_CS_NUM];
+	u32 val;
+	u32 pup_index;
+	int max_phase = MIN_VALUE, current_phase;
+	enum hws_access_type access_type = ACCESS_TYPE_UNICAST;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       DUNIT_ODT_CTRL_REG,
+				       0 << 8, 0x3 << 8));
+	CHECK_STATUS(ddr3_tip_if_read(dev_num, access_type, if_id,
+				      RD_DATA_SMPL_DLYS_REG,
+				      data_read, MASK_ALL_BITS));
+	val = data_read[if_id];
+
+	for (cs_num = 0; cs_num < MAX_CS_NUM; cs_num++) {
+		read_sample[cs_num] = GET_RD_SAMPLE_DELAY(val, cs_num);
+
+		/* find maximum of read_samples */
+		if (read_sample[cs_num] >= max_read_sample) {
+			if (read_sample[cs_num] == max_read_sample)
+				max_phase = MIN_VALUE;
+			else
+				max_read_sample = read_sample[cs_num];
+
+			for (pup_index = 0;
+			     pup_index < octets_per_if_num;
+			     pup_index++) {
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, pup_index,
+					      DDR_PHY_DATA,
+					      RL_PHY_REG(cs_num),
+					      &val));
+
+				current_phase = ((int)val & 0xe0) >> 6;
+				if (current_phase >= max_phase)
+					max_phase = current_phase;
+			}
+		}
+
+		/* find minimum */
+		if (read_sample[cs_num] < min_read_sample)
+			min_read_sample = read_sample[cs_num];
+	}
+
+	min_read_sample = min_read_sample - 1;
+	max_read_sample = max_read_sample + 4 + (max_phase + 1) / 2 + 1;
+	if (min_read_sample >= 0xf)
+		min_read_sample = 0xf;
+	if (max_read_sample >= 0x1f)
+		max_read_sample = 0x1f;
+
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       DDR_ODT_TIMING_LOW_REG,
+				       ((min_read_sample - 1) << 12),
+				       0xf << 12));
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id,
+				       DDR_ODT_TIMING_LOW_REG,
+				       (max_read_sample << 16),
+				       0x1f << 16));
+
+	return MV_OK;
+}
+
+int get_valid_win_rx(u32 dev_num, u32 if_id, u8 res[4])
+{
+	u32 reg_pup = RESULT_PHY_REG;
+	u32 reg_data;
+	u32 cs_num;
+	int i;
+
+	cs_num = 0;
+
+	/* TBD */
+	reg_pup += cs_num;
+
+	for (i = 0; i < 4; i++) {
+		CHECK_STATUS(ddr3_tip_bus_read(dev_num, if_id,
+					       ACCESS_TYPE_UNICAST, i,
+					       DDR_PHY_DATA, reg_pup,
+					       &reg_data));
+		res[i] = (reg_data >> RESULT_PHY_RX_OFFS) & 0x1f;
+	}
+
+	return 0;
+}
+
+/*
+ * This algorithm deals with the vertical optimum from Voltage point of view
+ * of the sample signal.
+ * Voltage sample point can improve the Eye / window size of the bit and the
+ * pup.
+ * The problem is that it is tune for all DQ the same so there isn't any
+ * PBS like code.
+ * It is more like centralization.
+ * But because we don't have The training SM support we do it a bit more
+ * smart search to save time.
+ */
+int ddr3_tip_vref(u32 dev_num)
+{
+	/*
+	 * The Vref register have non linear order. Need to check what will be
+	 * in future projects.
+	 */
+	u32 vref_map[8] = {
+		1, 2, 3, 4, 5, 6, 7, 0
+	};
+	/* State and parameter definitions */
+	u32 initial_step = VREF_INITIAL_STEP;
+	/* need to be assign with minus ????? */
+	u32 second_step = VREF_SECOND_STEP;
+	u32 algo_run_flag = 0, currrent_vref = 0;
+	u32 while_count = 0;
+	u32 pup = 0, if_id = 0, num_pup = 0, rep = 0;
+	u32 val = 0;
+	u32 reg_addr = 0xa8;
+	u32 copy_start_pattern, copy_end_pattern;
+	enum hws_result *flow_result = ddr3_tip_get_result_ptr(training_stage);
+	u8 res[4];
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	CHECK_STATUS(ddr3_tip_special_rx(dev_num));
+
+	/* save start/end pattern */
+	copy_start_pattern = start_pattern;
+	copy_end_pattern = end_pattern;
+
+	/* set vref as centralization pattern */
+	start_pattern = PATTERN_VREF;
+	end_pattern = PATTERN_VREF;
+
+	/* init params */
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup = 0;
+		     pup < octets_per_if_num; pup++) {
+			current_vref[pup][if_id] = 0;
+			last_vref[pup][if_id] = 0;
+			lim_vref[pup][if_id] = 0;
+			current_valid_window[pup][if_id] = 0;
+			last_valid_window[pup][if_id] = 0;
+			if (vref_window_size[if_id][pup] >
+			    vref_window_size_th) {
+				pup_st[pup][if_id] = VREF_CONVERGE;
+				DEBUG_TRAINING_HW_ALG(
+					DEBUG_LEVEL_INFO,
+					("VREF config, IF[ %d ]pup[ %d ] - Vref tune not requered (%d)\n",
+					 if_id, pup, __LINE__));
+			} else {
+				pup_st[pup][if_id] = VREF_STEP_1;
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr, &val));
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      pup, DDR_PHY_DATA, reg_addr,
+					      (val & (~0xf)) | vref_map[0]));
+				DEBUG_TRAINING_HW_ALG(
+					DEBUG_LEVEL_INFO,
+					("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n",
+					 if_id, pup,
+					 (val & (~0xf)) | vref_map[0],
+					 __LINE__));
+			}
+		}
+		interface_state[if_id] = 0;
+	}
+
+	/* TODO: Set number of active interfaces */
+	num_pup = octets_per_if_num * MAX_INTERFACE_NUM;
+
+	while ((algo_run_flag <= num_pup) & (while_count < 10)) {
+		while_count++;
+		for (rep = 1; rep < 4; rep++) {
+			ddr3_tip_centr_skip_min_win_check = 1;
+			ddr3_tip_centralization_rx(dev_num);
+			ddr3_tip_centr_skip_min_win_check = 0;
+
+			/* Read Valid window results only for non converge pups */
+			for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				if (interface_state[if_id] != 4) {
+					get_valid_win_rx(dev_num, if_id, res);
+					for (pup = 0;
+					     pup < octets_per_if_num;
+					     pup++) {
+						VALIDATE_BUS_ACTIVE
+							(tm->bus_act_mask, pup);
+						if (pup_st[pup]
+						    [if_id] ==
+						    VREF_CONVERGE)
+							continue;
+
+						current_valid_window[pup]
+							[if_id] =
+							(current_valid_window[pup]
+							 [if_id] * (rep - 1) +
+							 1000 * res[pup]) / rep;
+					}
+				}
+			}
+		}
+
+		for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			DEBUG_TRAINING_HW_ALG(
+				DEBUG_LEVEL_TRACE,
+				("current_valid_window: IF[ %d ] - ", if_id));
+
+			for (pup = 0;
+			     pup < octets_per_if_num; pup++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+				DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE,
+						      ("%d ",
+						       current_valid_window
+						       [pup][if_id]));
+			}
+			DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE, ("\n"));
+		}
+
+		/* Compare results and respond as function of state */
+		for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			for (pup = 0;
+			     pup < octets_per_if_num; pup++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+				DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE,
+						      ("I/F[ %d ], pup[ %d ] STATE #%d (%d)\n",
+						       if_id, pup,
+						       pup_st[pup]
+						       [if_id], __LINE__));
+
+				if (pup_st[pup][if_id] == VREF_CONVERGE)
+					continue;
+
+				DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE,
+						      ("I/F[ %d ], pup[ %d ] CHECK progress - Current %d Last %d, limit VREF %d (%d)\n",
+						       if_id, pup,
+						       current_valid_window[pup]
+						       [if_id],
+						       last_valid_window[pup]
+						       [if_id], lim_vref[pup]
+						       [if_id], __LINE__));
+
+				/*
+				 * The -1 is for solution resolution +/- 1 tap
+				 * of ADLL
+				 */
+				if (current_valid_window[pup][if_id] + 200 >=
+				    (last_valid_window[pup][if_id])) {
+					if (pup_st[pup][if_id] == VREF_STEP_1) {
+						/*
+						 * We stay in the same state and
+						 * step just update the window
+						 * size (take the max) and Vref
+						 */
+						if (current_vref[pup]
+						    [if_id] == VREF_MAX_INDEX) {
+							/*
+							 * If we step to the end
+							 * and didn't converge
+							 * to some particular
+							 * better Vref value
+							 * define the pup as
+							 * converge and step
+							 * back to nominal
+							 * Vref.
+							 */
+							pup_st[pup]
+								[if_id] =
+								VREF_CONVERGE;
+							algo_run_flag++;
+							interface_state
+								[if_id]++;
+							DEBUG_TRAINING_HW_ALG
+								(DEBUG_LEVEL_TRACE,
+								 ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n",
+								  if_id, pup,
+								  current_vref[pup]
+								  [if_id],
+								  __LINE__));
+						} else {
+							/* continue to update the Vref index */
+							current_vref[pup]
+								[if_id] =
+								((current_vref[pup]
+								  [if_id] +
+								  initial_step) >
+								 VREF_MAX_INDEX) ?
+								VREF_MAX_INDEX
+								: (current_vref[pup]
+								   [if_id] +
+								   initial_step);
+							if (current_vref[pup]
+							    [if_id] ==
+							    VREF_MAX_INDEX) {
+								pup_st[pup]
+									[if_id]
+									=
+									VREF_STEP_2;
+							}
+							lim_vref[pup]
+								[if_id] =
+								last_vref[pup]
+								[if_id] =
+								current_vref[pup]
+								[if_id];
+						}
+
+						last_valid_window[pup]
+							[if_id] =
+							GET_MAX(current_valid_window
+								[pup][if_id],
+								last_valid_window
+								[pup]
+								[if_id]);
+
+						/* update the Vref for next stage */
+						currrent_vref =
+							current_vref[pup]
+							[if_id];
+						CHECK_STATUS
+							(ddr3_tip_bus_read
+							 (dev_num, if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  &val));
+						CHECK_STATUS
+							(ddr3_tip_bus_write
+							 (dev_num,
+							  ACCESS_TYPE_UNICAST,
+							  if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  (val & (~0xf)) |
+							  vref_map[currrent_vref]));
+						DEBUG_TRAINING_HW_ALG
+							(DEBUG_LEVEL_TRACE,
+							 ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n",
+							  if_id, pup,
+							  (val & (~0xf)) |
+							  vref_map[currrent_vref],
+							  __LINE__));
+					} else if (pup_st[pup][if_id]
+						   == VREF_STEP_2) {
+						/*
+						 * We keep on search back with
+						 * the same step size.
+						 */
+						last_valid_window[pup]
+							[if_id] =
+							GET_MAX(current_valid_window
+								[pup][if_id],
+								last_valid_window
+								[pup]
+								[if_id]);
+						last_vref[pup][if_id] =
+							current_vref[pup]
+							[if_id];
+
+						/* we finish all search space */
+						if ((current_vref[pup]
+						     [if_id] - second_step) == lim_vref[pup][if_id]) {
+							/*
+							 * If we step to the end
+							 * and didn't converge
+							 * to some particular
+							 * better Vref value
+							 * define the pup as
+							 * converge and step
+							 * back to nominal
+							 * Vref.
+							 */
+							pup_st[pup]
+								[if_id] =
+								VREF_CONVERGE;
+							algo_run_flag++;
+
+							interface_state
+								[if_id]++;
+
+							current_vref[pup]
+								[if_id] =
+								(current_vref[pup]
+								 [if_id] -
+								 second_step);
+
+							DEBUG_TRAINING_HW_ALG
+								(DEBUG_LEVEL_TRACE,
+								 ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n",
+								  if_id, pup,
+								  current_vref[pup]
+								  [if_id],
+								  __LINE__));
+						} else
+							/* we finish all search space */
+							if (current_vref[pup]
+							    [if_id] ==
+							    lim_vref[pup]
+							    [if_id]) {
+								/*
+								 * If we step to the end
+								 * and didn't converge
+								 * to some particular
+								 * better Vref value
+								 * define the pup as
+								 * converge and step
+								 * back to nominal
+								 * Vref.
+								 */
+								pup_st[pup]
+									[if_id] =
+									VREF_CONVERGE;
+
+								algo_run_flag++;
+								interface_state
+									[if_id]++;
+								DEBUG_TRAINING_HW_ALG
+									(DEBUG_LEVEL_TRACE,
+									 ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n",
+									  if_id, pup,
+									  current_vref[pup]
+									  [if_id],
+									  __LINE__));
+							} else {
+								current_vref[pup]
+									[if_id] =
+									current_vref[pup]
+									[if_id] -
+									second_step;
+							}
+
+						/* Update the Vref for next stage */
+						currrent_vref =
+							current_vref[pup]
+							[if_id];
+						CHECK_STATUS
+							(ddr3_tip_bus_read
+							 (dev_num, if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  &val));
+						CHECK_STATUS
+							(ddr3_tip_bus_write
+							 (dev_num,
+							  ACCESS_TYPE_UNICAST,
+							  if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  (val & (~0xf)) |
+							  vref_map[currrent_vref]));
+						DEBUG_TRAINING_HW_ALG
+							(DEBUG_LEVEL_TRACE,
+							 ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n",
+							  if_id, pup,
+							  (val & (~0xf)) |
+							  vref_map[currrent_vref],
+							  __LINE__));
+					}
+				} else {
+					/* we change state and change step */
+					if (pup_st[pup][if_id] == VREF_STEP_1) {
+						pup_st[pup][if_id] =
+							VREF_STEP_2;
+						lim_vref[pup][if_id] =
+							current_vref[pup]
+							[if_id] - initial_step;
+						last_valid_window[pup]
+							[if_id] =
+							current_valid_window[pup]
+							[if_id];
+						last_vref[pup][if_id] =
+							current_vref[pup]
+							[if_id];
+						current_vref[pup][if_id] =
+							last_vref[pup][if_id] -
+							second_step;
+
+						/* Update the Vref for next stage */
+						CHECK_STATUS
+							(ddr3_tip_bus_read
+							 (dev_num, if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  &val));
+						CHECK_STATUS
+							(ddr3_tip_bus_write
+							 (dev_num,
+							  ACCESS_TYPE_UNICAST,
+							  if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  (val & (~0xf)) |
+							  vref_map[current_vref[pup]
+								   [if_id]]));
+						DEBUG_TRAINING_HW_ALG
+							(DEBUG_LEVEL_TRACE,
+							 ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n",
+							  if_id, pup,
+							  (val & (~0xf)) |
+							  vref_map[current_vref[pup]
+								   [if_id]],
+							  __LINE__));
+
+					} else if (pup_st[pup][if_id] == VREF_STEP_2) {
+						/*
+						 * The last search was the max
+						 * point set value and exit
+						 */
+						CHECK_STATUS
+							(ddr3_tip_bus_read
+							 (dev_num, if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  &val));
+						CHECK_STATUS
+							(ddr3_tip_bus_write
+							 (dev_num,
+							  ACCESS_TYPE_UNICAST,
+							  if_id,
+							  ACCESS_TYPE_UNICAST, pup,
+							  DDR_PHY_DATA, reg_addr,
+							  (val & (~0xf)) |
+							  vref_map[last_vref[pup]
+								   [if_id]]));
+						DEBUG_TRAINING_HW_ALG
+							(DEBUG_LEVEL_TRACE,
+							 ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n",
+							  if_id, pup,
+							  (val & (~0xf)) |
+							  vref_map[last_vref[pup]
+								   [if_id]],
+							  __LINE__));
+						pup_st[pup][if_id] =
+							VREF_CONVERGE;
+						algo_run_flag++;
+						interface_state[if_id]++;
+						DEBUG_TRAINING_HW_ALG
+							(DEBUG_LEVEL_TRACE,
+							 ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n",
+							  if_id, pup,
+							  current_vref[pup]
+							  [if_id], __LINE__));
+					}
+				}
+			}
+		}
+	}
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup = 0;
+		     pup < octets_per_if_num; pup++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id,
+				      ACCESS_TYPE_UNICAST, pup,
+				      DDR_PHY_DATA, reg_addr, &val));
+			DEBUG_TRAINING_HW_ALG(
+				DEBUG_LEVEL_INFO,
+				("FINAL values: I/F[ %d ], pup[ %d ] - Vref = %X (%d)\n",
+				 if_id, pup, val, __LINE__));
+		}
+	}
+
+	flow_result[if_id] = TEST_SUCCESS;
+
+	/* restore start/end pattern */
+	start_pattern = copy_start_pattern;
+	end_pattern = copy_end_pattern;
+
+	return 0;
+}
+
+/*
+ * CK/CA Delay
+ */
+int ddr3_tip_cmd_addr_init_delay(u32 dev_num, u32 adll_tap)
+{
+	u32 if_id = 0;
+	u32 ck_num_adll_tap = 0, ca_num_adll_tap = 0, data = 0;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/*
+	 * ck_delay_table is delaying the of the clock signal only.
+	 * (to overcome timing issues between_c_k & command/address signals)
+	 */
+	/*
+	 * ca_delay is delaying the of the entire command & Address signals
+	 * (include Clock signal to overcome DGL error on the Clock versus
+	 * the DQS).
+	 */
+
+	/* Calc ADLL Tap */
+	if (ck_delay == PARAM_UNDEFINED)
+		DEBUG_TRAINING_HW_ALG(
+			DEBUG_LEVEL_ERROR,
+			("ERROR: ck_delay is not initialized!\n"));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		/* Calc delay ps in ADLL tap */
+		ck_num_adll_tap = ck_delay / adll_tap;
+		ca_num_adll_tap = ca_delay / adll_tap;
+
+		data = (ck_num_adll_tap & 0x3f) +
+			((ca_num_adll_tap & 0x3f) << 10);
+
+		/*
+		 * Set the ADLL number to the CK ADLL for Interfaces for
+		 * all Pup
+		 */
+		DEBUG_TRAINING_HW_ALG(
+			DEBUG_LEVEL_TRACE,
+			("ck_num_adll_tap %d ca_num_adll_tap %d adll_tap %d\n",
+			 ck_num_adll_tap, ca_num_adll_tap, adll_tap));
+
+		CHECK_STATUS(ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST,
+						if_id, ACCESS_TYPE_MULTICAST,
+						PARAM_NOT_CARE, DDR_PHY_CONTROL,
+						0x0, data));
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.h b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.h
new file mode 100644
index 0000000..fe04366
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_HW_ALGO_H_
+#define _DDR3_TRAINING_HW_ALGO_H_
+
+int ddr3_tip_vref(u32 dev_num);
+int ddr3_tip_write_additional_odt_setting(u32 dev_num, u32 if_id);
+int ddr3_tip_cmd_addr_init_delay(u32 dev_num, u32 adll_tap);
+
+#endif /* _DDR3_TRAINING_HW_ALGO_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip.h b/drivers/ddr/marvell/a38x/ddr3_training_ip.h
new file mode 100644
index 0000000..056c214
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_H_
+#define _DDR3_TRAINING_IP_H_
+
+#include "ddr_topology_def.h"
+
+#define TIP_ENG_LOCK	0x02000000
+#define TIP_TX_DLL_RANGE_MAX	64
+
+#define GET_MIN(arg1, arg2)	((arg1) < (arg2)) ? (arg1) : (arg2)
+#define GET_MAX(arg1, arg2)	((arg1) < (arg2)) ? (arg2) : (arg1)
+
+#define INIT_CONTROLLER_MASK_BIT	0x00000001
+#define STATIC_LEVELING_MASK_BIT	0x00000002
+#define SET_LOW_FREQ_MASK_BIT		0x00000004
+#define LOAD_PATTERN_MASK_BIT		0x00000008
+#define SET_MEDIUM_FREQ_MASK_BIT	0x00000010
+#define WRITE_LEVELING_MASK_BIT		0x00000020
+#define LOAD_PATTERN_2_MASK_BIT		0x00000040
+#define READ_LEVELING_MASK_BIT		0x00000080
+#define SW_READ_LEVELING_MASK_BIT	0x00000100
+#define WRITE_LEVELING_SUPP_MASK_BIT	0x00000200
+#define PBS_RX_MASK_BIT			0x00000400
+#define PBS_TX_MASK_BIT			0x00000800
+#define SET_TARGET_FREQ_MASK_BIT	0x00001000
+#define ADJUST_DQS_MASK_BIT		0x00002000
+#define WRITE_LEVELING_TF_MASK_BIT	0x00004000
+#define LOAD_PATTERN_HIGH_MASK_BIT	0x00008000
+#define READ_LEVELING_TF_MASK_BIT	0x00010000
+#define WRITE_LEVELING_SUPP_TF_MASK_BIT	0x00020000
+#define DM_PBS_TX_MASK_BIT		0x00040000
+#define RL_DQS_BURST_MASK_BIT		0x00080000
+#define CENTRALIZATION_RX_MASK_BIT	0x00100000
+#define CENTRALIZATION_TX_MASK_BIT	0x00200000
+#define TX_EMPHASIS_MASK_BIT		0x00400000
+#define PER_BIT_READ_LEVELING_TF_MASK_BIT	0x00800000
+#define VREF_CALIBRATION_MASK_BIT	0x01000000
+#define WRITE_LEVELING_LF_MASK_BIT	0x02000000
+
+/* DDR4 Specific Training Mask bits */
+
+enum hws_result {
+	TEST_FAILED = 0,
+	TEST_SUCCESS = 1,
+	NO_TEST_DONE = 2
+};
+
+enum hws_training_result {
+	RESULT_PER_BIT,
+	RESULT_PER_BYTE
+};
+
+enum auto_tune_stage {
+	INIT_CONTROLLER,
+	STATIC_LEVELING,
+	SET_LOW_FREQ,
+	LOAD_PATTERN,
+	SET_MEDIUM_FREQ,
+	WRITE_LEVELING,
+	LOAD_PATTERN_2,
+	READ_LEVELING,
+	WRITE_LEVELING_SUPP,
+	PBS_RX,
+	PBS_TX,
+	SET_TARGET_FREQ,
+	ADJUST_DQS,
+	WRITE_LEVELING_TF,
+	READ_LEVELING_TF,
+	WRITE_LEVELING_SUPP_TF,
+	DM_PBS_TX,
+	VREF_CALIBRATION,
+	CENTRALIZATION_RX,
+	CENTRALIZATION_TX,
+	TX_EMPHASIS,
+	LOAD_PATTERN_HIGH,
+	PER_BIT_READ_LEVELING_TF,
+	WRITE_LEVELING_LF,
+	MAX_STAGE_LIMIT
+};
+
+enum hws_access_type {
+	ACCESS_TYPE_UNICAST = 0,
+	ACCESS_TYPE_MULTICAST = 1
+};
+
+enum hws_algo_type {
+	ALGO_TYPE_DYNAMIC,
+	ALGO_TYPE_STATIC
+};
+
+struct init_cntr_param {
+	int is_ctrl64_bit;
+	int do_mrs_phy;
+	int init_phy;
+	int msys_init;
+};
+
+struct pattern_info {
+	u8 num_of_phases_tx;
+	u8 tx_burst_size;
+	u8 delay_between_bursts;
+	u8 num_of_phases_rx;
+	u32 start_addr;
+	u8 pattern_len;
+};
+
+struct cs_element {
+	u8 cs_num;
+	u8 num_of_cs;
+};
+
+struct hws_tip_freq_config_info {
+	u8 is_supported;
+	u8 bw_per_freq;
+	u8 rate_per_freq;
+};
+
+struct hws_cs_config_info {
+	u32 cs_reg_value;
+	u32 cs_cbe_value;
+};
+
+struct dfx_access {
+	u8 pipe;
+	u8 client;
+};
+
+struct hws_xsb_info {
+	struct dfx_access *dfx_table;
+};
+
+int ddr3_tip_register_dq_table(u32 dev_num, u32 *table);
+int hws_ddr3_tip_select_ddr_controller(u32 dev_num, int enable);
+int hws_ddr3_tip_init_controller(u32 dev_num,
+				 struct init_cntr_param *init_cntr_prm);
+int hws_ddr3_tip_load_topology_map(u32 dev_num,
+				   struct mv_ddr_topology_map *topology);
+int hws_ddr3_tip_run_alg(u32 dev_num, enum hws_algo_type algo_type);
+int ddr3_tip_is_pup_lock(u32 *pup_buf, enum hws_training_result read_mode);
+u8 ddr3_tip_get_buf_min(u8 *buf_ptr);
+u8 ddr3_tip_get_buf_max(u8 *buf_ptr);
+#endif /* _DDR3_TRAINING_IP_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_bist.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_bist.h
new file mode 100644
index 0000000..38058cb
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_bist.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_BIST_H_
+#define _DDR3_TRAINING_IP_BIST_H_
+
+#include "ddr3_training_ip.h"
+
+enum hws_bist_operation {
+	BIST_STOP = 0,
+	BIST_START = 1
+};
+
+enum  hws_stress_jump {
+	STRESS_NONE = 0,
+	STRESS_ENABLE = 1
+};
+
+enum hws_pattern_duration {
+	DURATION_SINGLE = 0,
+	DURATION_STOP_AT_FAIL = 1,
+	DURATION_ADDRESS = 2,
+	DURATION_CONT = 4
+};
+
+struct bist_result {
+	u32 bist_error_cnt;
+	u32 bist_fail_low;
+	u32 bist_fail_high;
+	u32 bist_last_fail_addr;
+};
+
+int ddr3_tip_bist_read_result(u32 dev_num, u32 if_id,
+			      struct bist_result *pst_bist_result);
+int ddr3_tip_bist_activate(u32 dev_num, enum hws_pattern pattern,
+			   enum hws_access_type access_type,
+			   u32 if_num, enum hws_dir direction,
+			   enum hws_stress_jump addr_stress_jump,
+			   enum hws_pattern_duration duration,
+			   enum hws_bist_operation oper_type,
+			   u32 offset, u32 cs_num, u32 pattern_addr_length);
+int hws_ddr3_run_bist(u32 dev_num, enum hws_pattern pattern, u32 *result,
+		      u32 cs_num);
+int ddr3_tip_run_sweep_test(int dev_num, u32 repeat_num, u32 direction,
+			    u32 mode);
+int ddr3_tip_run_leveling_sweep_test(int dev_num, u32 repeat_num,
+				     u32 direction, u32 mode);
+int ddr3_tip_print_regs(u32 dev_num);
+int ddr3_tip_reg_dump(u32 dev_num);
+int run_xsb_test(u32 dev_num, u32 mem_addr, u32 write_type, u32 read_type,
+		 u32 burst_length);
+int mv_ddr_dm_to_dq_diff_get(u8 adll_byte_high, u8 adll_byte_low, u8 *vw_vector,
+			     int *delta_h_adll, int *delta_l_adll);
+int mv_ddr_dm_vw_get(enum hws_pattern pattern, u32 cs, u8 *vw_vector);
+#endif /* _DDR3_TRAINING_IP_BIST_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_centralization.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_centralization.h
new file mode 100644
index 0000000..392842a
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_centralization.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_CENTRALIZATION_H
+#define _DDR3_TRAINING_IP_CENTRALIZATION_H
+
+int ddr3_tip_centralization_tx(u32 dev_num);
+int ddr3_tip_centralization_rx(u32 dev_num);
+int ddr3_tip_print_centralization_result(u32 dev_num);
+int ddr3_tip_special_rx(u32 dev_num);
+
+#endif /* _DDR3_TRAINING_IP_CENTRALIZATION_H */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_db.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_db.h
new file mode 100644
index 0000000..e28b7ec
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_db.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_DB_H_
+#define _DDR3_TRAINING_IP_DB_H_
+
+enum hws_pattern {
+	PATTERN_PBS1,
+	PATTERN_PBS2,
+	PATTERN_PBS3,
+	PATTERN_TEST,
+	PATTERN_RL,
+	PATTERN_RL2,
+	PATTERN_STATIC_PBS,
+	PATTERN_KILLER_DQ0,
+	PATTERN_KILLER_DQ1,
+	PATTERN_KILLER_DQ2,
+	PATTERN_KILLER_DQ3,
+	PATTERN_KILLER_DQ4,
+	PATTERN_KILLER_DQ5,
+	PATTERN_KILLER_DQ6,
+	PATTERN_KILLER_DQ7,
+	PATTERN_VREF,
+	PATTERN_FULL_SSO0,
+	PATTERN_FULL_SSO1,
+	PATTERN_FULL_SSO2,
+	PATTERN_FULL_SSO3,
+	PATTERN_LAST,
+	PATTERN_SSO_FULL_XTALK_DQ0,
+	PATTERN_SSO_FULL_XTALK_DQ1,
+	PATTERN_SSO_FULL_XTALK_DQ2,
+	PATTERN_SSO_FULL_XTALK_DQ3,
+	PATTERN_SSO_FULL_XTALK_DQ4,
+	PATTERN_SSO_FULL_XTALK_DQ5,
+	PATTERN_SSO_FULL_XTALK_DQ6,
+	PATTERN_SSO_FULL_XTALK_DQ7,
+	PATTERN_SSO_XTALK_FREE_DQ0,
+	PATTERN_SSO_XTALK_FREE_DQ1,
+	PATTERN_SSO_XTALK_FREE_DQ2,
+	PATTERN_SSO_XTALK_FREE_DQ3,
+	PATTERN_SSO_XTALK_FREE_DQ4,
+	PATTERN_SSO_XTALK_FREE_DQ5,
+	PATTERN_SSO_XTALK_FREE_DQ6,
+	PATTERN_SSO_XTALK_FREE_DQ7,
+	PATTERN_ISI_XTALK_FREE
+};
+
+enum mv_wl_supp_mode {
+	WRITE_LEVELING_SUPP_REG_MODE,
+	WRITE_LEVELING_SUPP_ECC_MODE_DATA_PUPS,
+	WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP4,
+	WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP3,
+	WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8
+};
+
+enum mv_ddr_dev_attribute {
+	MV_ATTR_TIP_REV,
+	MV_ATTR_PHY_EDGE,
+	MV_ATTR_OCTET_PER_INTERFACE,
+	MV_ATTR_PLL_BEFORE_INIT,
+	MV_ATTR_TUNE_MASK,
+	MV_ATTR_INIT_FREQ,
+	MV_ATTR_MID_FREQ,
+	MV_ATTR_DFS_LOW_FREQ,
+	MV_ATTR_DFS_LOW_PHY,
+	MV_ATTR_DELAY_ENABLE,
+	MV_ATTR_CK_DELAY,
+	MV_ATTR_CA_DELAY,
+	MV_ATTR_INTERLEAVE_WA,
+	MV_ATTR_LAST
+};
+
+enum mv_ddr_tip_revison {
+	MV_TIP_REV_NA,
+	MV_TIP_REV_1, /* NP5 */
+	MV_TIP_REV_2, /* BC2 */
+	MV_TIP_REV_3, /* AC3 */
+	MV_TIP_REV_4, /* A-380/A-390 */
+	MV_TIP_REV_LAST
+};
+
+enum mv_ddr_phy_edge {
+	MV_DDR_PHY_EDGE_POSITIVE,
+	MV_DDR_PHY_EDGE_NEGATIVE
+};
+
+/* Device attribute functions */
+void ddr3_tip_dev_attr_init(u32 dev_num);
+u32 ddr3_tip_dev_attr_get(u32 dev_num, enum mv_ddr_dev_attribute attr_id);
+void ddr3_tip_dev_attr_set(u32 dev_num, enum mv_ddr_dev_attribute attr_id, u32 value);
+
+#endif /* _DDR3_TRAINING_IP_DB_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_def.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_def.h
new file mode 100644
index 0000000..2a68669
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_def.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_DEF_H
+#define _DDR3_TRAINING_IP_DEF_H
+
+#define PATTERN_55			0x55555555
+#define PATTERN_AA			0xaaaaaaaa
+#define PATTERN_80			0x80808080
+#define PATTERN_20			0x20202020
+#define PATTERN_01			0x01010101
+#define PATTERN_FF			0xffffffff
+#define PATTERN_00			0x00000000
+
+/* 16bit bus width patterns */
+#define PATTERN_55AA			0x5555aaaa
+#define PATTERN_00FF			0x0000ffff
+#define PATTERN_0080			0x00008080
+
+#define INVALID_VALUE			0xffffffff
+#define MAX_NUM_OF_DUNITS		32
+/*
+ * length *2 = length in words of pattern, first low address,
+ * second high address
+ */
+#define TEST_PATTERN_LENGTH		4
+#define KILLER_PATTERN_DQ_NUMBER	8
+#define SSO_DQ_NUMBER			4
+#define PATTERN_MAXIMUM_LENGTH		64
+#define ADLL_TX_LENGTH			64
+#define ADLL_RX_LENGTH			32
+
+#define PARAM_NOT_CARE			0
+#define PARAM_UNDEFINED			0xffffffff
+
+#define READ_LEVELING_PHY_OFFSET	2
+#define WRITE_LEVELING_PHY_OFFSET	0
+
+#define MASK_ALL_BITS			0xffffffff
+
+#define CS_BIT_MASK			0xf
+
+/* DFX access */
+#define BROADCAST_ID			28
+#define MULTICAST_ID			29
+
+#define XSB_BASE_ADDR			0x00004000
+#define XSB_CTRL_0_REG			0x00000000
+#define XSB_CTRL_1_REG			0x00000004
+#define XSB_CMD_REG			0x00000008
+#define XSB_ADDRESS_REG			0x0000000c
+#define XSB_DATA_REG			0x00000010
+#define PIPE_ENABLE_ADDR		0x000f8000
+#define ENABLE_DDR_TUNING_ADDR		0x000f829c
+
+#define CLIENT_BASE_ADDR		0x00002000
+#define CLIENT_CTRL_REG			0x00000000
+
+#define TARGET_INT			0x1801
+#define TARGET_EXT			0x180e
+#define BYTE_EN				0
+#define CMD_READ			0
+#define CMD_WRITE			1
+
+#define INTERNAL_ACCESS_PORT		1
+#define EXECUTING			1
+#define ACCESS_EXT			1
+#define CS2_EXIST_BIT			2
+#define TRAINING_ID			0xf
+#define EXT_TRAINING_ID			1
+#define EXT_MODE			0x4
+
+#define GET_RESULT_STATE(res)		(res)
+#define SET_RESULT_STATE(res, state)	(res = state)
+
+#define ADDR_SIZE_512MB			0x04000000
+#define ADDR_SIZE_1GB			0x08000000
+#define ADDR_SIZE_2GB			0x10000000
+#define ADDR_SIZE_4GB			0x20000000
+#define ADDR_SIZE_8GB			0x40000000
+
+enum hws_edge_compare {
+	EDGE_PF,
+	EDGE_FP,
+	EDGE_FPF,
+	EDGE_PFP
+};
+
+enum hws_control_element {
+	HWS_CONTROL_ELEMENT_ADLL,		/* per bit 1 edge */
+	HWS_CONTROL_ELEMENT_DQ_SKEW,
+	HWS_CONTROL_ELEMENT_DQS_SKEW
+};
+
+enum hws_search_dir {
+	HWS_LOW2HIGH,
+	HWS_HIGH2LOW,
+	HWS_SEARCH_DIR_LIMIT
+};
+
+enum hws_operation {
+	OPERATION_READ = 0,
+	OPERATION_WRITE = 1
+};
+
+enum hws_training_ip_stat {
+	HWS_TRAINING_IP_STATUS_FAIL,
+	HWS_TRAINING_IP_STATUS_SUCCESS,
+	HWS_TRAINING_IP_STATUS_TIMEOUT
+};
+
+enum hws_ddr_cs {
+	CS_SINGLE,
+	CS_NON_SINGLE
+};
+
+enum hws_ddr_phy {
+	DDR_PHY_DATA = 0,
+	DDR_PHY_CONTROL = 1
+};
+
+enum hws_dir {
+	OPER_WRITE,
+	OPER_READ,
+	OPER_WRITE_AND_READ
+};
+
+enum hws_wl_supp {
+	PHASE_SHIFT,
+	CLOCK_SHIFT,
+	ALIGN_SHIFT
+};
+
+enum  mv_ddr_tip_bit_state {
+	BIT_LOW_UI,
+	BIT_HIGH_UI,
+	BIT_SPLIT_IN,
+	BIT_SPLIT_OUT,
+	BIT_STATE_LAST
+};
+
+enum  mv_ddr_tip_byte_state{
+	BYTE_NOT_DEFINED,
+	BYTE_HOMOGENEOUS_LOW = 0x1,
+	BYTE_HOMOGENEOUS_HIGH = 0x2,
+	BYTE_HOMOGENEOUS_SPLIT_IN = 0x4,
+	BYTE_HOMOGENEOUS_SPLIT_OUT = 0x8,
+	BYTE_SPLIT_OUT_MIX = 0x10,
+	BYTE_STATE_LAST
+};
+
+struct reg_data {
+	unsigned int reg_addr;
+	unsigned int reg_data;
+	unsigned int reg_mask;
+};
+
+enum dm_direction {
+	DM_DIR_INVERSE,
+	DM_DIR_DIRECT
+};
+
+#endif /* _DDR3_TRAINING_IP_DEF_H */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.c b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.c
new file mode 100644
index 0000000..74417d6
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.c
@@ -0,0 +1,1677 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_regs.h"
+#include "ddr_training_ip_db.h"
+
+#define PATTERN_1	0x55555555
+#define PATTERN_2	0xaaaaaaaa
+
+#define VALIDATE_TRAINING_LIMIT(e1, e2)			\
+	((((e2) - (e1) + 1) > 33) && ((e1) < 67))
+
+u32 phy_reg_bk[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS];
+
+u32 training_res[MAX_INTERFACE_NUM * MAX_BUS_NUM * BUS_WIDTH_IN_BITS *
+		 HWS_SEARCH_DIR_LIMIT];
+u8 byte_status[MAX_INTERFACE_NUM][MAX_BUS_NUM];	/* holds the bit status in the byte in wrapper function*/
+
+u16 mask_results_dq_reg_map[] = {
+	RESULT_CONTROL_PUP_0_BIT_0_REG, RESULT_CONTROL_PUP_0_BIT_1_REG,
+	RESULT_CONTROL_PUP_0_BIT_2_REG, RESULT_CONTROL_PUP_0_BIT_3_REG,
+	RESULT_CONTROL_PUP_0_BIT_4_REG, RESULT_CONTROL_PUP_0_BIT_5_REG,
+	RESULT_CONTROL_PUP_0_BIT_6_REG, RESULT_CONTROL_PUP_0_BIT_7_REG,
+	RESULT_CONTROL_PUP_1_BIT_0_REG, RESULT_CONTROL_PUP_1_BIT_1_REG,
+	RESULT_CONTROL_PUP_1_BIT_2_REG, RESULT_CONTROL_PUP_1_BIT_3_REG,
+	RESULT_CONTROL_PUP_1_BIT_4_REG, RESULT_CONTROL_PUP_1_BIT_5_REG,
+	RESULT_CONTROL_PUP_1_BIT_6_REG, RESULT_CONTROL_PUP_1_BIT_7_REG,
+	RESULT_CONTROL_PUP_2_BIT_0_REG, RESULT_CONTROL_PUP_2_BIT_1_REG,
+	RESULT_CONTROL_PUP_2_BIT_2_REG, RESULT_CONTROL_PUP_2_BIT_3_REG,
+	RESULT_CONTROL_PUP_2_BIT_4_REG, RESULT_CONTROL_PUP_2_BIT_5_REG,
+	RESULT_CONTROL_PUP_2_BIT_6_REG, RESULT_CONTROL_PUP_2_BIT_7_REG,
+	RESULT_CONTROL_PUP_3_BIT_0_REG, RESULT_CONTROL_PUP_3_BIT_1_REG,
+	RESULT_CONTROL_PUP_3_BIT_2_REG, RESULT_CONTROL_PUP_3_BIT_3_REG,
+	RESULT_CONTROL_PUP_3_BIT_4_REG, RESULT_CONTROL_PUP_3_BIT_5_REG,
+	RESULT_CONTROL_PUP_3_BIT_6_REG, RESULT_CONTROL_PUP_3_BIT_7_REG,
+	RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG,
+	RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG,
+	RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG,
+	RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG,
+#if MAX_BUS_NUM == 9
+	RESULT_CONTROL_PUP_5_BIT_0_REG, RESULT_CONTROL_PUP_5_BIT_1_REG,
+	RESULT_CONTROL_PUP_5_BIT_2_REG, RESULT_CONTROL_PUP_5_BIT_3_REG,
+	RESULT_CONTROL_PUP_5_BIT_4_REG, RESULT_CONTROL_PUP_5_BIT_5_REG,
+	RESULT_CONTROL_PUP_5_BIT_6_REG, RESULT_CONTROL_PUP_5_BIT_7_REG,
+	RESULT_CONTROL_PUP_6_BIT_0_REG, RESULT_CONTROL_PUP_6_BIT_1_REG,
+	RESULT_CONTROL_PUP_6_BIT_2_REG, RESULT_CONTROL_PUP_6_BIT_3_REG,
+	RESULT_CONTROL_PUP_6_BIT_4_REG, RESULT_CONTROL_PUP_6_BIT_5_REG,
+	RESULT_CONTROL_PUP_6_BIT_6_REG, RESULT_CONTROL_PUP_6_BIT_7_REG,
+	RESULT_CONTROL_PUP_7_BIT_0_REG, RESULT_CONTROL_PUP_7_BIT_1_REG,
+	RESULT_CONTROL_PUP_7_BIT_2_REG, RESULT_CONTROL_PUP_7_BIT_3_REG,
+	RESULT_CONTROL_PUP_7_BIT_4_REG, RESULT_CONTROL_PUP_7_BIT_5_REG,
+	RESULT_CONTROL_PUP_7_BIT_6_REG, RESULT_CONTROL_PUP_7_BIT_7_REG,
+	RESULT_CONTROL_PUP_8_BIT_0_REG, RESULT_CONTROL_PUP_8_BIT_1_REG,
+	RESULT_CONTROL_PUP_8_BIT_2_REG, RESULT_CONTROL_PUP_8_BIT_3_REG,
+	RESULT_CONTROL_PUP_8_BIT_4_REG, RESULT_CONTROL_PUP_8_BIT_5_REG,
+	RESULT_CONTROL_PUP_8_BIT_6_REG, RESULT_CONTROL_PUP_8_BIT_7_REG,
+#endif
+	0xffff
+};
+
+u16 mask_results_pup_reg_map[] = {
+	RESULT_CONTROL_BYTE_PUP_0_REG, RESULT_CONTROL_BYTE_PUP_1_REG,
+	RESULT_CONTROL_BYTE_PUP_2_REG, RESULT_CONTROL_BYTE_PUP_3_REG,
+	RESULT_CONTROL_BYTE_PUP_4_REG,
+#if MAX_BUS_NUM == 9
+	RESULT_CONTROL_BYTE_PUP_5_REG, RESULT_CONTROL_BYTE_PUP_6_REG,
+	RESULT_CONTROL_BYTE_PUP_7_REG, RESULT_CONTROL_BYTE_PUP_8_REG,
+#endif
+	0xffff
+};
+
+#if MAX_BUS_NUM == 5
+u16 mask_results_dq_reg_map_pup3_ecc[] = {
+	RESULT_CONTROL_PUP_0_BIT_0_REG, RESULT_CONTROL_PUP_0_BIT_1_REG,
+	RESULT_CONTROL_PUP_0_BIT_2_REG, RESULT_CONTROL_PUP_0_BIT_3_REG,
+	RESULT_CONTROL_PUP_0_BIT_4_REG, RESULT_CONTROL_PUP_0_BIT_5_REG,
+	RESULT_CONTROL_PUP_0_BIT_6_REG, RESULT_CONTROL_PUP_0_BIT_7_REG,
+	RESULT_CONTROL_PUP_1_BIT_0_REG, RESULT_CONTROL_PUP_1_BIT_1_REG,
+	RESULT_CONTROL_PUP_1_BIT_2_REG, RESULT_CONTROL_PUP_1_BIT_3_REG,
+	RESULT_CONTROL_PUP_1_BIT_4_REG, RESULT_CONTROL_PUP_1_BIT_5_REG,
+	RESULT_CONTROL_PUP_1_BIT_6_REG, RESULT_CONTROL_PUP_1_BIT_7_REG,
+	RESULT_CONTROL_PUP_2_BIT_0_REG, RESULT_CONTROL_PUP_2_BIT_1_REG,
+	RESULT_CONTROL_PUP_2_BIT_2_REG, RESULT_CONTROL_PUP_2_BIT_3_REG,
+	RESULT_CONTROL_PUP_2_BIT_4_REG, RESULT_CONTROL_PUP_2_BIT_5_REG,
+	RESULT_CONTROL_PUP_2_BIT_6_REG, RESULT_CONTROL_PUP_2_BIT_7_REG,
+	RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG,
+	RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG,
+	RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG,
+	RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG,
+	RESULT_CONTROL_PUP_3_BIT_0_REG, RESULT_CONTROL_PUP_3_BIT_1_REG,
+	RESULT_CONTROL_PUP_3_BIT_2_REG, RESULT_CONTROL_PUP_3_BIT_3_REG,
+	RESULT_CONTROL_PUP_3_BIT_4_REG, RESULT_CONTROL_PUP_3_BIT_5_REG,
+	RESULT_CONTROL_PUP_3_BIT_6_REG, RESULT_CONTROL_PUP_3_BIT_7_REG
+};
+#endif
+
+#if MAX_BUS_NUM == 5
+u16 mask_results_pup_reg_map_pup3_ecc[] = {
+	RESULT_CONTROL_BYTE_PUP_0_REG, RESULT_CONTROL_BYTE_PUP_1_REG,
+	RESULT_CONTROL_BYTE_PUP_2_REG, RESULT_CONTROL_BYTE_PUP_4_REG,
+	RESULT_CONTROL_BYTE_PUP_4_REG
+};
+#endif
+
+struct pattern_info pattern_table_64[] = {
+	/*
+	 * num_of_phases_tx, tx_burst_size;
+	 * delay_between_bursts, num_of_phases_rx,
+	 * start_addr, pattern_len
+	 */
+	{0x7, 0x7, 2, 0x7, 0x00000, 8},		/* PATTERN_PBS1 */
+	{0x7, 0x7, 2, 0x7, 0x00080, 8},		/* PATTERN_PBS2 */
+	{0x7, 0x7, 2, 0x7, 0x00100, 8},		/* PATTERN_PBS3 */
+	{0x7, 0x7, 2, 0x7, 0x00030, 8},		/* PATTERN_TEST */
+	{0x7, 0x7, 2, 0x7, 0x00100, 8},		/* PATTERN_RL */
+	{0x7, 0x7, 2, 0x7, 0x00100, 8},		/* PATTERN_RL2 */
+	{0x1f, 0xf, 2, 0xf, 0x00680, 32},	/* PATTERN_STATIC_PBS */
+	{0x1f, 0xf, 2, 0xf, 0x00a80, 32},	/* PATTERN_KILLER_DQ0 */
+	{0x1f, 0xf, 2, 0xf, 0x01280, 32},	/* PATTERN_KILLER_DQ1 */
+	{0x1f, 0xf, 2, 0xf, 0x01a80, 32},	/* PATTERN_KILLER_DQ2 */
+	{0x1f, 0xf, 2, 0xf, 0x02280, 32},	/* PATTERN_KILLER_DQ3 */
+	{0x1f, 0xf, 2, 0xf, 0x02a80, 32},	/* PATTERN_KILLER_DQ4 */
+	{0x1f, 0xf, 2, 0xf, 0x03280, 32},	/* PATTERN_KILLER_DQ5 */
+	{0x1f, 0xf, 2, 0xf, 0x03a80, 32},	/* PATTERN_KILLER_DQ6 */
+	{0x1f, 0xf, 2, 0xf, 0x04280, 32},	/* PATTERN_KILLER_DQ7 */
+	{0x1f, 0xf, 2, 0xf, 0x00e80, 32},	/* PATTERN_KILLER_DQ0_64 */
+	{0x1f, 0xf, 2, 0xf, 0x01680, 32},	/* PATTERN_KILLER_DQ1_64 */
+	{0x1f, 0xf, 2, 0xf, 0x01e80, 32},	/* PATTERN_KILLER_DQ2_64 */
+	{0x1f, 0xf, 2, 0xf, 0x02680, 32},	/* PATTERN_KILLER_DQ3_64 */
+	{0x1f, 0xf, 2, 0xf, 0x02e80, 32},	/* PATTERN_KILLER_DQ4_64 */
+	{0x1f, 0xf, 2, 0xf, 0x03680, 32},	/* PATTERN_KILLER_DQ5_64 */
+	{0x1f, 0xf, 2, 0xf, 0x03e80, 32},	/* PATTERN_KILLER_DQ6_64 */
+	{0x1f, 0xf, 2, 0xf, 0x04680, 32},	/* PATTERN_KILLER_DQ7_64 */
+	{0x1f, 0xf, 2, 0xf, 0x04a80, 32},	/* PATTERN_KILLER_DQ0_INV */
+	{0x1f, 0xf, 2, 0xf, 0x05280, 32},	/* PATTERN_KILLER_DQ1_INV */
+	{0x1f, 0xf, 2, 0xf, 0x05a80, 32},	/* PATTERN_KILLER_DQ2_INV */
+	{0x1f, 0xf, 2, 0xf, 0x06280, 32},	/* PATTERN_KILLER_DQ3_INV */
+	{0x1f, 0xf, 2, 0xf, 0x06a80, 32},	/* PATTERN_KILLER_DQ4_INV */
+	{0x1f, 0xf, 2, 0xf, 0x07280, 32},	/* PATTERN_KILLER_DQ5_INV */
+	{0x1f, 0xf, 2, 0xf, 0x07a80, 32},	/* PATTERN_KILLER_DQ6_INV */
+	{0x1f, 0xf, 2, 0xf, 0x08280, 32},	/* PATTERN_KILLER_DQ7_INV */
+	{0x1f, 0xf, 2, 0xf, 0x04e80, 32},	/* PATTERN_KILLER_DQ0_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x05680, 32},	/* PATTERN_KILLER_DQ1_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x05e80, 32},	/* PATTERN_KILLER_DQ2_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x06680, 32},	/* PATTERN_KILLER_DQ3_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x06e80, 32},	/* PATTERN_KILLER_DQ4_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x07680, 32},	/* PATTERN_KILLER_DQ5_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x07e80, 32},	/* PATTERN_KILLER_DQ6_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x08680, 32},	/* PATTERN_KILLER_DQ7_INV_64 */
+	{0x1f, 0xf, 2, 0xf, 0x08a80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ0 */
+	{0x1f, 0xf, 2, 0xf, 0x09280, 32},	/* PATTERN_SSO_FULL_XTALK_DQ1 */
+	{0x1f, 0xf, 2, 0xf, 0x09a80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ2 */
+	{0x1f, 0xf, 2, 0xf, 0x0a280, 32},	/* PATTERN_SSO_FULL_XTALK_DQ3 */
+	{0x1f, 0xf, 2, 0xf, 0x0aa80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ4 */
+	{0x1f, 0xf, 2, 0xf, 0x0b280, 32},	/* PATTERN_SSO_FULL_XTALK_DQ5 */
+	{0x1f, 0xf, 2, 0xf, 0x0ba80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ6 */
+	{0x1f, 0xf, 2, 0xf, 0x0c280, 32},	/* PATTERN_SSO_FULL_XTALK_DQ7 */
+	{0x1f, 0xf, 2, 0xf, 0x08e80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ0_64 */
+	{0x1f, 0xf, 2, 0xf, 0x09680, 32},	/* PATTERN_SSO_FULL_XTALK_DQ1_64 */
+	{0x1f, 0xf, 2, 0xf, 0x09e80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ2_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0a680, 32},	/* PATTERN_SSO_FULL_XTALK_DQ3_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0ae80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ4_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0b680, 32},	/* PATTERN_SSO_FULL_XTALK_DQ5_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0be80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ6_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0c680, 32},	/* PATTERN_SSO_FULL_XTALK_DQ7_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0ca80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ0 */
+	{0x1f, 0xf, 2, 0xf, 0x0d280, 32},	/* PATTERN_SSO_XTALK_FREE_DQ1 */
+	{0x1f, 0xf, 2, 0xf, 0x0da80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ2 */
+	{0x1f, 0xf, 2, 0xf, 0x0e280, 32},	/* PATTERN_SSO_XTALK_FREE_DQ3 */
+	{0x1f, 0xf, 2, 0xf, 0x0ea80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ4 */
+	{0x1f, 0xf, 2, 0xf, 0x0f280, 32},	/* PATTERN_SSO_XTALK_FREE_DQ5 */
+	{0x1f, 0xf, 2, 0xf, 0x0fa80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ6 */
+	{0x1f, 0xf, 2, 0xf, 0x10280, 32},	/* PATTERN_SSO_XTALK_FREE_DQ7 */
+	{0x1f, 0xf, 2, 0xf, 0x0ce80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ0_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0d680, 32},	/* PATTERN_SSO_XTALK_FREE_DQ1_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0de80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ2_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0e680, 32},	/* PATTERN_SSO_XTALK_FREE_DQ3_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0ee80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ4_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0f680, 32},	/* PATTERN_SSO_XTALK_FREE_DQ5_64 */
+	{0x1f, 0xf, 2, 0xf, 0x0fe80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ6_64 */
+	{0x1f, 0xf, 2, 0xf, 0x10680, 32},	/* PATTERN_SSO_XTALK_FREE_DQ7_64 */
+	{0x1f, 0xf, 2, 0xf, 0x10a80, 32},	/* PATTERN_ISI_XTALK_FREE */
+	{0x1f, 0xf, 2, 0xf, 0x10e80, 32},	/* PATTERN_ISI_XTALK_FREE_64 */
+	{0x1f, 0xf, 2, 0xf, 0x11280, 32},	/* PATTERN_VREF */
+	{0x1f, 0xf, 2, 0xf, 0x11680, 32},	/* PATTERN_VREF_64 */
+	{0x1f, 0xf, 2, 0xf, 0x11a80, 32},	/* PATTERN_VREF_INV */
+	{0x1f, 0xf, 2, 0xf, 0x11e80, 32},	/* PATTERN_FULL_SSO_0T */
+	{0x1f, 0xf, 2, 0xf, 0x12280, 32},	/* PATTERN_FULL_SSO_1T */
+	{0x1f, 0xf, 2, 0xf, 0x12680, 32},	/* PATTERN_FULL_SSO_2T */
+	{0x1f, 0xf, 2, 0xf, 0x12a80, 32},	/* PATTERN_FULL_SSO_3T */
+	{0x1f, 0xf, 2, 0xf, 0x12e80, 32},	/* PATTERN_RESONANCE_1T */
+	{0x1f, 0xf, 2, 0xf, 0x13280, 32},	/* PATTERN_RESONANCE_2T */
+	{0x1f, 0xf, 2, 0xf, 0x13680, 32},	/* PATTERN_RESONANCE_3T */
+	{0x1f, 0xf, 2, 0xf, 0x13a80, 32},	/* PATTERN_RESONANCE_4T */
+	{0x1f, 0xf, 2, 0xf, 0x13e80, 32},	/* PATTERN_RESONANCE_5T */
+	{0x1f, 0xf, 2, 0xf, 0x14280, 32},	/* PATTERN_RESONANCE_6T */
+	{0x1f, 0xf, 2, 0xf, 0x14680, 32},	/* PATTERN_RESONANCE_7T */
+	{0x1f, 0xf, 2, 0xf, 0x14a80, 32},	/* PATTERN_RESONANCE_8T */
+	{0x1f, 0xf, 2, 0xf, 0x14e80, 32},	/* PATTERN_RESONANCE_9T */
+	{0x1f, 0xf, 2, 0xf, 0x15280, 32},	/* PATTERN_ZERO */
+	{0x1f, 0xf, 2, 0xf, 0x15680, 32}	/* PATTERN_ONE */
+	/* Note: actual start_address is "<< 3" of defined address */
+};
+
+struct pattern_info pattern_table_16[] = {
+	/*
+	 * num tx phases, tx burst, delay between, rx pattern,
+	 * start_address, pattern_len
+	 */
+	{1, 1, 2, 1, 0x0080, 2},	/* PATTERN_PBS1 */
+	{1, 1, 2, 1, 0x00c0, 2},	/* PATTERN_PBS2 */
+	{1, 1, 2, 1, 0x0380, 2},	/* PATTERN_PBS3 */
+	{1, 1, 2, 1, 0x0040, 2},	/* PATTERN_TEST */
+	{1, 1, 2, 1, 0x0100, 2},	/* PATTERN_RL */
+	{1, 1, 2, 1, 0x0000, 2},	/* PATTERN_RL2 */
+	{0xf, 0x7, 2, 0x7, 0x0140, 16},	/* PATTERN_STATIC_PBS */
+	{0xf, 0x7, 2, 0x7, 0x0190, 16},	/* PATTERN_KILLER_DQ0 */
+	{0xf, 0x7, 2, 0x7, 0x01d0, 16},	/* PATTERN_KILLER_DQ1 */
+	{0xf, 0x7, 2, 0x7, 0x0210, 16},	/* PATTERN_KILLER_DQ2 */
+	{0xf, 0x7, 2, 0x7, 0x0250, 16},	/* PATTERN_KILLER_DQ3 */
+	{0xf, 0x7, 2, 0x7, 0x0290, 16},	/* PATTERN_KILLER_DQ4 */
+	{0xf, 0x7, 2, 0x7, 0x02d0, 16},	/* PATTERN_KILLER_DQ5 */
+	{0xf, 0x7, 2, 0x7, 0x0310, 16},	/* PATTERN_KILLER_DQ6 */
+	{0xf, 0x7, 2, 0x7, 0x0350, 16},	/* PATTERN_KILLER_DQ7 */
+	{0xf, 0x7, 2, 0x7, 0x04c0, 16},	/* PATTERN_VREF */
+	{0xf, 0x7, 2, 0x7, 0x03c0, 16},	/* PATTERN_FULL_SSO_1T */
+	{0xf, 0x7, 2, 0x7, 0x0400, 16},	/* PATTERN_FULL_SSO_2T */
+	{0xf, 0x7, 2, 0x7, 0x0440, 16},	/* PATTERN_FULL_SSO_3T */
+	{0xf, 0x7, 2, 0x7, 0x0480, 16},	/* PATTERN_FULL_SSO_4T */
+	{0xf, 7, 2, 7, 0x6280, 16},	/* PATTERN_SSO_FULL_XTALK_DQ1 */
+	{0xf, 7, 2, 7, 0x6680, 16},	/* PATTERN_SSO_FULL_XTALK_DQ1 */
+	{0xf, 7, 2, 7, 0x6A80, 16},	/* PATTERN_SSO_FULL_XTALK_DQ2 */
+	{0xf, 7, 2, 7, 0x6E80, 16},	/* PATTERN_SSO_FULL_XTALK_DQ3 */
+	{0xf, 7, 2, 7, 0x7280, 16},	/* PATTERN_SSO_FULL_XTALK_DQ4 */
+	{0xf, 7, 2, 7, 0x7680, 16},	/* PATTERN_SSO_FULL_XTALK_DQ5 */
+	{0xf, 7, 2, 7, 0x7A80, 16},	/* PATTERN_SSO_FULL_XTALK_DQ6 */
+	{0xf, 7, 2, 7, 0x7E80, 16},	/* PATTERN_SSO_FULL_XTALK_DQ7 */
+	{0xf, 7, 2, 7, 0x8280, 16},	/* PATTERN_SSO_XTALK_FREE_DQ0 */
+	{0xf, 7, 2, 7, 0x8680, 16},	/* PATTERN_SSO_XTALK_FREE_DQ1 */
+	{0xf, 7, 2, 7, 0x8A80, 16},	/* PATTERN_SSO_XTALK_FREE_DQ2 */
+	{0xf, 7, 2, 7, 0x8E80, 16},	/* PATTERN_SSO_XTALK_FREE_DQ3 */
+	{0xf, 7, 2, 7, 0x9280, 16},	/* PATTERN_SSO_XTALK_FREE_DQ4 */
+	{0xf, 7, 2, 7, 0x9680, 16},	/* PATTERN_SSO_XTALK_FREE_DQ5 */
+	{0xf, 7, 2, 7, 0x9A80, 16},	/* PATTERN_SSO_XTALK_FREE_DQ6 */
+	{0xf, 7, 2, 7, 0x9E80, 16},	/* PATTERN_SSO_XTALK_FREE_DQ7 */
+	{0xf, 7, 2, 7, 0xA280, 16}	/* PATTERN_ISI_XTALK_FREE */
+	/* Note: actual start_address is "<< 3" of defined address */
+};
+
+struct pattern_info pattern_table_32[] = {
+	/*
+	 * num tx phases, tx burst, delay between, rx pattern,
+	 * start_address, pattern_len
+	 */
+	{3, 3, 2, 3, 0x0080, 4},	/* PATTERN_PBS1 */
+	{3, 3, 2, 3, 0x00c0, 4},	/* PATTERN_PBS2 */
+	{3, 3, 2, 3, 0x0380, 4},	/* PATTERN_PBS3 */
+	{3, 3, 2, 3, 0x0040, 4},	/* PATTERN_TEST */
+	{3, 3, 2, 3, 0x0100, 4},	/* PATTERN_RL */
+	{3, 3, 2, 3, 0x0000, 4},	/* PATTERN_RL2 */
+	{0x1f, 0xf, 2, 0xf, 0x0140, 32},	/* PATTERN_STATIC_PBS */
+	{0x1f, 0xf, 2, 0xf, 0x0190, 32},	/* PATTERN_KILLER_DQ0 */
+	{0x1f, 0xf, 2, 0xf, 0x01d0, 32},	/* PATTERN_KILLER_DQ1 */
+	{0x1f, 0xf, 2, 0xf, 0x0210, 32},	/* PATTERN_KILLER_DQ2 */
+	{0x1f, 0xf, 2, 0xf, 0x0250, 32},	/* PATTERN_KILLER_DQ3 */
+	{0x1f, 0xf, 2, 0xf, 0x0290, 32},	/* PATTERN_KILLER_DQ4 */
+	{0x1f, 0xf, 2, 0xf, 0x02d0, 32},	/* PATTERN_KILLER_DQ5 */
+	{0x1f, 0xf, 2, 0xf, 0x0310, 32},	/* PATTERN_KILLER_DQ6 */
+	{0x1f, 0xf, 2, 0xf, 0x0350, 32},	/* PATTERN_KILLER_DQ7 */
+	{0x1f, 0xf, 2, 0xf, 0x04c0, 32},	/* PATTERN_VREF */
+	{0x1f, 0xf, 2, 0xf, 0x03c0, 32},	/* PATTERN_FULL_SSO_1T */
+	{0x1f, 0xf, 2, 0xf, 0x0400, 32},	/* PATTERN_FULL_SSO_2T */
+	{0x1f, 0xf, 2, 0xf, 0x0440, 32},	/* PATTERN_FULL_SSO_3T */
+	{0x1f, 0xf, 2, 0xf, 0x0480, 32},	/* PATTERN_FULL_SSO_4T */
+	{0x1f, 0xF, 2, 0xf, 0x6280, 32},	/* PATTERN_SSO_FULL_XTALK_DQ0 */
+	{0x1f, 0xF, 2, 0xf, 0x6680, 32},	/* PATTERN_SSO_FULL_XTALK_DQ1 */
+	{0x1f, 0xF, 2, 0xf, 0x6A80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ2 */
+	{0x1f, 0xF, 2, 0xf, 0x6E80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ3 */
+	{0x1f, 0xF, 2, 0xf, 0x7280, 32},	/* PATTERN_SSO_FULL_XTALK_DQ4 */
+	{0x1f, 0xF, 2, 0xf, 0x7680, 32},	/* PATTERN_SSO_FULL_XTALK_DQ5 */
+	{0x1f, 0xF, 2, 0xf, 0x7A80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ6 */
+	{0x1f, 0xF, 2, 0xf, 0x7E80, 32},	/* PATTERN_SSO_FULL_XTALK_DQ7 */
+	{0x1f, 0xF, 2, 0xf, 0x8280, 32},	/* PATTERN_SSO_XTALK_FREE_DQ0 */
+	{0x1f, 0xF, 2, 0xf, 0x8680, 32},	/* PATTERN_SSO_XTALK_FREE_DQ1 */
+	{0x1f, 0xF, 2, 0xf, 0x8A80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ2 */
+	{0x1f, 0xF, 2, 0xf, 0x8E80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ3 */
+	{0x1f, 0xF, 2, 0xf, 0x9280, 32},	/* PATTERN_SSO_XTALK_FREE_DQ4 */
+	{0x1f, 0xF, 2, 0xf, 0x9680, 32},	/* PATTERN_SSO_XTALK_FREE_DQ5 */
+	{0x1f, 0xF, 2, 0xf, 0x9A80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ6 */
+	{0x1f, 0xF, 2, 0xf, 0x9E80, 32},	/* PATTERN_SSO_XTALK_FREE_DQ7 */
+	{0x1f, 0xF, 2, 0xf, 0xA280, 32}		/* PATTERN_ISI_XTALK_FREE */
+	/* Note: actual start_address is "<< 3" of defined address */
+};
+
+u32 train_dev_num;
+enum hws_ddr_cs traintrain_cs_type;
+u32 train_pup_num;
+enum hws_training_result train_result_type;
+enum hws_control_element train_control_element;
+enum hws_search_dir traine_search_dir;
+enum hws_dir train_direction;
+u32 train_if_select;
+u32 train_init_value;
+u32 train_number_iterations;
+enum hws_pattern train_pattern;
+enum hws_edge_compare train_edge_compare;
+u32 train_cs_num;
+u32 train_if_acess, train_if_id, train_pup_access;
+u32 max_polling_for_done = 1000000;
+
+u32 *ddr3_tip_get_buf_ptr(u32 dev_num, enum hws_search_dir search,
+			  enum hws_training_result result_type,
+			  u32 interface_num)
+{
+	u32 *buf_ptr = NULL;
+
+	buf_ptr = &training_res
+		[MAX_INTERFACE_NUM * MAX_BUS_NUM * BUS_WIDTH_IN_BITS * search +
+		 interface_num * MAX_BUS_NUM * BUS_WIDTH_IN_BITS];
+
+	return buf_ptr;
+}
+
+enum {
+	PASS,
+	FAIL
+};
+/*
+ * IP Training search
+ * Note: for one edge search only from fail to pass, else jitter can
+ * be be entered into solution.
+ */
+int ddr3_tip_ip_training(u32 dev_num, enum hws_access_type access_type,
+			 u32 interface_num,
+			 enum hws_access_type pup_access_type,
+			 u32 pup_num, enum hws_training_result result_type,
+			 enum hws_control_element control_element,
+			 enum hws_search_dir search_dir, enum hws_dir direction,
+			 u32 interface_mask, u32 init_value, u32 num_iter,
+			 enum hws_pattern pattern,
+			 enum hws_edge_compare edge_comp,
+			 enum hws_ddr_cs cs_type, u32 cs_num,
+			 enum hws_training_ip_stat *train_status)
+{
+	u32 mask_dq_num_of_regs, mask_pup_num_of_regs, index_cnt,
+		reg_data, pup_id;
+	u32 tx_burst_size;
+	u32 delay_between_burst;
+	u32 rd_mode;
+	u32 data;
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (pup_num >= octets_per_if_num) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("pup_num %d not valid\n", pup_num));
+	}
+	if (interface_num >= MAX_INTERFACE_NUM) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("if_id %d not valid\n",
+					  interface_num));
+	}
+	if (train_status == NULL) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("error param 4\n"));
+		return MV_BAD_PARAM;
+	}
+
+	/* load pattern */
+	if (cs_type == CS_SINGLE) {
+		/* All CSs to CS0     */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, interface_num,
+			      DUAL_DUNIT_CFG_REG, 1 << 3, 1 << 3));
+		/* All CSs to CS0     */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, interface_num,
+			      ODPG_DATA_CTRL_REG,
+			      (0x3 | (effective_cs << 26)), 0xc000003));
+	} else {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, interface_num,
+			      DUAL_DUNIT_CFG_REG, 0, 1 << 3));
+		/*  CS select */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, interface_num,
+			      ODPG_DATA_CTRL_REG, 0x3 | cs_num << 26,
+			      0x3 | 3 << 26));
+	}
+
+	/* load pattern to ODPG */
+	ddr3_tip_load_pattern_to_odpg(dev_num, access_type, interface_num,
+				      pattern,
+				      pattern_table[pattern].start_addr);
+	tx_burst_size =	(direction == OPER_WRITE) ?
+		pattern_table[pattern].tx_burst_size : 0;
+	delay_between_burst = (direction == OPER_WRITE) ? 2 : 0;
+	rd_mode = (direction == OPER_WRITE) ? 1 : 0;
+	CHECK_STATUS(ddr3_tip_configure_odpg
+		     (dev_num, access_type, interface_num, direction,
+		      pattern_table[pattern].num_of_phases_tx, tx_burst_size,
+		      pattern_table[pattern].num_of_phases_rx,
+		      delay_between_burst, rd_mode, effective_cs, STRESS_NONE,
+		      DURATION_SINGLE));
+	reg_data = (direction == OPER_READ) ? 0 : (0x3 << 30);
+	reg_data |= (direction == OPER_READ) ? 0x60 : 0xfa;
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, interface_num,
+		      ODPG_WR_RD_MODE_ENA_REG, reg_data,
+		      MASK_ALL_BITS));
+	reg_data = (edge_comp == EDGE_PF || edge_comp == EDGE_FP) ? 0 : 1 << 6;
+	reg_data |= (edge_comp == EDGE_PF || edge_comp == EDGE_PFP) ?
+		(1 << 7) : 0;
+
+	/* change from Pass to Fail will lock the result */
+	if (pup_access_type == ACCESS_TYPE_MULTICAST)
+		reg_data |= 0xe << 14;
+	else
+		reg_data |= pup_num << 14;
+
+	if (edge_comp == EDGE_FP) {
+		/* don't search for readl edge change, only the state */
+		reg_data |= (0 << 20);
+	} else if (edge_comp == EDGE_FPF) {
+		reg_data |= (0 << 20);
+	} else {
+		reg_data |= (3 << 20);
+	}
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, interface_num,
+		      GENERAL_TRAINING_OPCODE_REG,
+		      reg_data | (0x7 << 8) | (0x7 << 11),
+		      (0x3 | (0x3 << 2) | (0x3 << 6) | (1 << 5) | (0x7 << 8) |
+		       (0x7 << 11) | (0xf << 14) | (0x3 << 18) | (3 << 20))));
+	reg_data = (search_dir == HWS_LOW2HIGH) ? 0 : (1 << 8);
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, interface_num, OPCODE_REG0_REG(1),
+		      1 | reg_data | init_value << 9 | (1 << 25) | (1 << 26),
+		      0xff | (1 << 8) | (0xffff << 9) | (1 << 25) | (1 << 26)));
+
+	/*
+	 * Write2_dunit(0x10b4, Number_iteration , [15:0])
+	 * Max number of iterations
+	 */
+	CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, interface_num,
+				       OPCODE_REG1_REG(1), num_iter,
+				       0xffff));
+	if (control_element == HWS_CONTROL_ELEMENT_DQ_SKEW &&
+	    direction == OPER_READ) {
+		/*
+		 * Write2_dunit(0x10c0, 0x5f , [7:0])
+		 * MC PBS Reg Address at DDR PHY
+		 */
+		reg_data = PBS_RX_BCAST_PHY_REG(effective_cs);
+	} else if (control_element == HWS_CONTROL_ELEMENT_DQ_SKEW &&
+		   direction == OPER_WRITE) {
+		reg_data = PBS_TX_BCAST_PHY_REG(effective_cs);
+	} else if (control_element == HWS_CONTROL_ELEMENT_ADLL &&
+		   direction == OPER_WRITE) {
+		/*
+		 * LOOP         0x00000001 + 4*n:
+		 * where n (0-3) represents M_CS number
+		 */
+		/*
+		 * Write2_dunit(0x10c0, 0x1 , [7:0])
+		 * ADLL WR Reg Address at DDR PHY
+		 */
+		reg_data = CTX_PHY_REG(effective_cs);
+	} else if (control_element == HWS_CONTROL_ELEMENT_ADLL &&
+		   direction == OPER_READ) {
+		/* ADLL RD Reg Address at DDR PHY */
+		reg_data = CRX_PHY_REG(effective_cs);
+	} else if (control_element == HWS_CONTROL_ELEMENT_DQS_SKEW &&
+		   direction == OPER_WRITE) {
+		/* TBD not defined in 0.5.0 requirement  */
+	} else if (control_element == HWS_CONTROL_ELEMENT_DQS_SKEW &&
+		   direction == OPER_READ) {
+		/* TBD not defined in 0.5.0 requirement */
+	}
+
+	reg_data |= (0x6 << 28);
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, interface_num, CAL_PHY_REG(1),
+		      reg_data | (init_value << 8),
+		      0xff | (0xffff << 8) | (0xf << 24) | (u32) (0xf << 28)));
+
+	mask_dq_num_of_regs = octets_per_if_num * BUS_WIDTH_IN_BITS;
+	mask_pup_num_of_regs = octets_per_if_num;
+
+	if (result_type == RESULT_PER_BIT) {
+		for (index_cnt = 0; index_cnt < mask_dq_num_of_regs;
+		     index_cnt++) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, interface_num,
+				      mask_results_dq_reg_map[index_cnt], 0,
+				      1 << 24));
+		}
+
+		/* Mask disabled buses */
+		for (pup_id = 0; pup_id < octets_per_if_num;
+		     pup_id++) {
+			if (IS_BUS_ACTIVE(tm->bus_act_mask, pup_id) == 1)
+				continue;
+
+			for (index_cnt = (pup_id * 8); index_cnt < (pup_id + 1) * 8; index_cnt++) {
+				CHECK_STATUS(ddr3_tip_if_write
+					     (dev_num, access_type,
+					      interface_num,
+					      mask_results_dq_reg_map
+					      [index_cnt], (1 << 24), 1 << 24));
+			}
+		}
+
+		for (index_cnt = 0; index_cnt < mask_pup_num_of_regs;
+		     index_cnt++) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, interface_num,
+				      mask_results_pup_reg_map[index_cnt],
+				      (1 << 24), 1 << 24));
+		}
+	} else if (result_type == RESULT_PER_BYTE) {
+		/* write to adll */
+		for (index_cnt = 0; index_cnt < mask_pup_num_of_regs;
+		     index_cnt++) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, interface_num,
+				      mask_results_pup_reg_map[index_cnt], 0,
+				      1 << 24));
+		}
+		for (index_cnt = 0; index_cnt < mask_dq_num_of_regs;
+		     index_cnt++) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, interface_num,
+				      mask_results_dq_reg_map[index_cnt],
+				      (1 << 24), (1 << 24)));
+		}
+	}
+
+	/* trigger training */
+	mv_ddr_training_enable();
+
+	/* wa for 16-bit mode: wait for all rfu tests to finish or timeout */
+	mdelay(1);
+
+	/* check for training done */
+	if (mv_ddr_is_training_done(MAX_POLLING_ITERATIONS, &data) != MV_OK) {
+		train_status[0] = HWS_TRAINING_IP_STATUS_TIMEOUT;
+	} else { /* training done; check for pass */
+		if (data == PASS)
+			train_status[0] = HWS_TRAINING_IP_STATUS_SUCCESS;
+		else
+			train_status[0] = HWS_TRAINING_IP_STATUS_FAIL;
+	}
+
+	ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			  ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS);
+
+	return MV_OK;
+}
+
+/*
+ * Load expected Pattern to ODPG
+ */
+int ddr3_tip_load_pattern_to_odpg(u32 dev_num, enum hws_access_type access_type,
+				  u32 if_id, enum hws_pattern pattern,
+				  u32 load_addr)
+{
+	u32 pattern_length_cnt = 0;
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (pattern_length_cnt = 0;
+	     pattern_length_cnt < pattern_table[pattern].pattern_len;
+	     pattern_length_cnt++) {	/* FIXME: the ecc patch below is only for a7040 A0 */
+		if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask)/* || tm->bus_act_mask == MV_DDR_32BIT_ECC_PUP8_BUS_MASK*/) {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      ODPG_DATA_WR_DATA_LOW_REG,
+				      pattern_table_get_word(dev_num, pattern,
+							     (u8) (pattern_length_cnt)),
+				      MASK_ALL_BITS));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      ODPG_DATA_WR_DATA_HIGH_REG,
+				      pattern_table_get_word(dev_num, pattern,
+							     (u8) (pattern_length_cnt)),
+				      MASK_ALL_BITS));
+		} else {
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+					      ODPG_DATA_WR_DATA_LOW_REG,
+				      pattern_table_get_word(dev_num, pattern,
+							     (u8) (pattern_length_cnt * 2)),
+				      MASK_ALL_BITS));
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, access_type, if_id,
+				      ODPG_DATA_WR_DATA_HIGH_REG,
+				      pattern_table_get_word(dev_num, pattern,
+							     (u8) (pattern_length_cnt * 2 + 1)),
+				      MASK_ALL_BITS));
+		}
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, access_type, if_id,
+			      ODPG_DATA_WR_ADDR_REG, pattern_length_cnt,
+			      MASK_ALL_BITS));
+	}
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, access_type, if_id,
+		      ODPG_DATA_BUFFER_OFFS_REG, load_addr, MASK_ALL_BITS));
+
+	return MV_OK;
+}
+
+/*
+ * Configure ODPG
+ */
+int ddr3_tip_configure_odpg(u32 dev_num, enum hws_access_type access_type,
+			    u32 if_id, enum hws_dir direction, u32 tx_phases,
+			    u32 tx_burst_size, u32 rx_phases,
+			    u32 delay_between_burst, u32 rd_mode, u32 cs_num,
+			    u32 addr_stress_jump, u32 single_pattern)
+{
+	u32 data_value = 0;
+	int ret;
+
+	data_value = ((single_pattern << 2) | (tx_phases << 5) |
+		      (tx_burst_size << 11) | (delay_between_burst << 15) |
+		      (rx_phases << 21) | (rd_mode << 25) | (cs_num << 26) |
+		      (addr_stress_jump << 29));
+	ret = ddr3_tip_if_write(dev_num, access_type, if_id,
+				ODPG_DATA_CTRL_REG, data_value, 0xaffffffc);
+	if (ret != MV_OK)
+		return ret;
+
+	return MV_OK;
+}
+
+int ddr3_tip_process_result(u32 *ar_result, enum hws_edge e_edge,
+			    enum hws_edge_search e_edge_search,
+			    u32 *edge_result)
+{
+	u32 i, res;
+	int tap_val, max_val = -10000, min_val = 10000;
+	int lock_success = 1;
+
+	for (i = 0; i < BUS_WIDTH_IN_BITS; i++) {
+		res = GET_LOCK_RESULT(ar_result[i]);
+		if (res == 0) {
+			lock_success = 0;
+			break;
+		}
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("lock failed for bit %d\n", i));
+	}
+
+	if (lock_success == 1) {
+		for (i = 0; i < BUS_WIDTH_IN_BITS; i++) {
+			tap_val = GET_TAP_RESULT(ar_result[i], e_edge);
+			if (tap_val > max_val)
+				max_val = tap_val;
+			if (tap_val < min_val)
+				min_val = tap_val;
+			if (e_edge_search == TRAINING_EDGE_MAX)
+				*edge_result = (u32) max_val;
+			else
+				*edge_result = (u32) min_val;
+
+			DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+						 ("i %d ar_result[i] 0x%x tap_val %d max_val %d min_val %d Edge_result %d\n",
+						  i, ar_result[i], tap_val,
+						  max_val, min_val,
+						  *edge_result));
+		}
+	} else {
+		return MV_FAIL;
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Read training search result
+ */
+int ddr3_tip_read_training_result(u32 dev_num, u32 if_id,
+				  enum hws_access_type pup_access_type,
+				  u32 pup_num, u32 bit_num,
+				  enum hws_search_dir search,
+				  enum hws_dir direction,
+				  enum hws_training_result result_type,
+				  enum hws_training_load_op operation,
+				  u32 cs_num_type, u32 **load_res,
+				  int is_read_from_db, u8 cons_tap,
+				  int is_check_result_validity)
+{
+	u32 reg_offset, pup_cnt, start_pup, end_pup, start_reg, end_reg;
+	u32 *interface_train_res = NULL;
+	u16 *reg_addr = NULL;
+	u32 read_data[MAX_INTERFACE_NUM];
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/*
+	 * Agreed assumption: all CS mask contain same number of bits,
+	 * i.e. in multi CS, the number of CS per memory is the same for
+	 * all pups
+	 */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_UNICAST, if_id, DUAL_DUNIT_CFG_REG,
+		      (cs_num_type == 0) ? 1 << 3 : 0, (1 << 3)));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+		      ODPG_DATA_CTRL_REG, (cs_num_type << 26), (3 << 26)));
+	DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_TRACE,
+				 ("Read_from_d_b %d cs_type %d oper %d result_type %d direction %d search %d pup_num %d if_id %d pup_access_type %d\n",
+				  is_read_from_db, cs_num_type, operation,
+				  result_type, direction, search, pup_num,
+				  if_id, pup_access_type));
+
+	if ((load_res == NULL) && (is_read_from_db == 1)) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("ddr3_tip_read_training_result load_res = NULL"));
+		return MV_FAIL;
+	}
+	if (pup_num >= octets_per_if_num) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("pup_num %d not valid\n", pup_num));
+	}
+	if (if_id >= MAX_INTERFACE_NUM) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("if_id %d not valid\n", if_id));
+	}
+	if (result_type == RESULT_PER_BIT)
+		reg_addr = mask_results_dq_reg_map;
+	else
+		reg_addr = mask_results_pup_reg_map;
+	if (pup_access_type == ACCESS_TYPE_UNICAST) {
+		start_pup = pup_num;
+		end_pup = pup_num;
+	} else {		/*pup_access_type == ACCESS_TYPE_MULTICAST) */
+
+		start_pup = 0;
+		end_pup = octets_per_if_num - 1;
+	}
+
+	for (pup_cnt = start_pup; pup_cnt <= end_pup; pup_cnt++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup_cnt);
+		DEBUG_TRAINING_IP_ENGINE(
+			DEBUG_LEVEL_TRACE,
+			("if_id %d start_pup %d end_pup %d pup_cnt %d\n",
+			 if_id, start_pup, end_pup, pup_cnt));
+		if (result_type == RESULT_PER_BIT) {
+			if (bit_num == ALL_BITS_PER_PUP) {
+				start_reg = pup_cnt * BUS_WIDTH_IN_BITS;
+				end_reg = (pup_cnt + 1) * BUS_WIDTH_IN_BITS - 1;
+			} else {
+				start_reg =
+					pup_cnt * BUS_WIDTH_IN_BITS + bit_num;
+				end_reg = pup_cnt * BUS_WIDTH_IN_BITS + bit_num;
+			}
+		} else {
+			start_reg = pup_cnt;
+			end_reg = pup_cnt;
+		}
+
+		interface_train_res =
+			ddr3_tip_get_buf_ptr(dev_num, search, result_type,
+					     if_id);
+		DEBUG_TRAINING_IP_ENGINE(
+			DEBUG_LEVEL_TRACE,
+			("start_reg %d end_reg %d interface %p\n",
+			 start_reg, end_reg, interface_train_res));
+		if (interface_train_res == NULL) {
+			DEBUG_TRAINING_IP_ENGINE(
+				DEBUG_LEVEL_ERROR,
+				("interface_train_res is NULL\n"));
+			return MV_FAIL;
+		}
+
+		for (reg_offset = start_reg; reg_offset <= end_reg;
+		     reg_offset++) {
+			if (operation == TRAINING_LOAD_OPERATION_UNLOAD) {
+				if (is_read_from_db == 0) {
+					CHECK_STATUS(ddr3_tip_if_read
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      reg_addr[reg_offset],
+						      read_data,
+						      MASK_ALL_BITS));
+					if (is_check_result_validity == 1) {
+						if ((read_data[if_id] &
+						     TIP_ENG_LOCK) == 0) {
+							interface_train_res
+								[reg_offset] =
+								TIP_ENG_LOCK +
+								TIP_TX_DLL_RANGE_MAX;
+						} else {
+							interface_train_res
+								[reg_offset] =
+								read_data
+								[if_id] +
+								cons_tap;
+						}
+					} else {
+						interface_train_res[reg_offset]
+							= read_data[if_id] +
+							cons_tap;
+					}
+					DEBUG_TRAINING_IP_ENGINE
+						(DEBUG_LEVEL_TRACE,
+						 ("reg_offset %d value 0x%x addr %p\n",
+						  reg_offset,
+						  interface_train_res
+						  [reg_offset],
+						  &interface_train_res
+						  [reg_offset]));
+				} else {
+					*load_res =
+						&interface_train_res[start_reg];
+					DEBUG_TRAINING_IP_ENGINE
+						(DEBUG_LEVEL_TRACE,
+						 ("*load_res %p\n", *load_res));
+				}
+			} else {
+				DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_TRACE,
+							 ("not supported\n"));
+			}
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Load all pattern to memory using ODPG
+ */
+int ddr3_tip_load_all_pattern_to_mem(u32 dev_num)
+{
+	u32 pattern = 0, if_id;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		training_result[training_stage][if_id] = TEST_SUCCESS;
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* enable single cs */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, (1 << 3), (1 << 3)));
+	}
+
+	for (pattern = 0; pattern < PATTERN_LAST; pattern++)
+		ddr3_tip_load_pattern_to_mem(dev_num, pattern);
+
+	return MV_OK;
+}
+
+/*
+ * Load specific pattern to memory using ODPG
+ */
+int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern pattern)
+{
+	u32 reg_data, if_id;
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* load pattern to memory */
+	/*
+	 * Write Tx mode, CS0, phases, Tx burst size, delay between burst,
+	 * rx pattern phases
+	 */
+	reg_data =
+		0x1 | (pattern_table[pattern].num_of_phases_tx << 5) |
+		(pattern_table[pattern].tx_burst_size << 11) |
+		(pattern_table[pattern].delay_between_bursts << 15) |
+		(pattern_table[pattern].num_of_phases_rx << 21) | (0x1 << 25) |
+		(effective_cs << 26);
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_CTRL_REG, reg_data, MASK_ALL_BITS));
+	/* ODPG Write enable from BIST */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_CTRL_REG, (0x1 | (effective_cs << 26)),
+		      0xc000003));
+	/* disable error injection */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_WR_DATA_ERR_REG, 0, 0x1));
+	/* load pattern to ODPG */
+	ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST,
+				      PARAM_NOT_CARE, pattern,
+				      pattern_table[pattern].start_addr);
+
+	if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) >= MV_TIP_REV_3) {
+		for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      SDRAM_ODT_CTRL_HIGH_REG,
+				      0x3, 0xf));
+		}
+
+		mv_ddr_odpg_enable();
+	} else {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_DATA_CTRL_REG, (u32)(0x1 << 31),
+			      (u32)(0x1 << 31)));
+	}
+	mdelay(1);
+
+	if (mv_ddr_is_odpg_done(MAX_POLLING_ITERATIONS) != MV_OK)
+		return MV_FAIL;
+
+	/* Disable ODPG and stop write to memory */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_CTRL_REG, (0x1 << 30), (u32) (0x3 << 30)));
+
+	/* return to default */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS));
+
+	if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) >= MV_TIP_REV_3) {
+		/* Disable odt0 for CS0 training - need to adjust for multy CS */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      SDRAM_ODT_CTRL_HIGH_REG, 0x0, 0xf));
+	}
+	/* temporary added */
+	mdelay(1);
+
+	return MV_OK;
+}
+
+/*
+ * Training search routine
+ */
+int ddr3_tip_ip_training_wrapper_int(u32 dev_num,
+				     enum hws_access_type access_type,
+				     u32 if_id,
+				     enum hws_access_type pup_access_type,
+				     u32 pup_num, u32 bit_num,
+				     enum hws_training_result result_type,
+				     enum hws_control_element control_element,
+				     enum hws_search_dir search_dir,
+				     enum hws_dir direction,
+				     u32 interface_mask, u32 init_value_l2h,
+				     u32 init_value_h2l, u32 num_iter,
+				     enum hws_pattern pattern,
+				     enum hws_edge_compare edge_comp,
+				     enum hws_ddr_cs train_cs_type, u32 cs_num,
+				     enum hws_training_ip_stat *train_status)
+{
+	u32 interface_num = 0, start_if, end_if, init_value_used;
+	enum hws_search_dir search_dir_id, start_search, end_search;
+	enum hws_edge_compare edge_comp_used;
+	u8 cons_tap = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (train_status == NULL) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+					 ("train_status is NULL\n"));
+		return MV_FAIL;
+	}
+
+	if ((train_cs_type > CS_NON_SINGLE) ||
+	    (edge_comp >= EDGE_PFP) ||
+	    (pattern >= PATTERN_LAST) ||
+	    (direction > OPER_WRITE_AND_READ) ||
+	    (search_dir > HWS_HIGH2LOW) ||
+	    (control_element > HWS_CONTROL_ELEMENT_DQS_SKEW) ||
+	    (result_type > RESULT_PER_BYTE) ||
+	    (pup_num >= octets_per_if_num) ||
+	    (pup_access_type > ACCESS_TYPE_MULTICAST) ||
+	    (if_id > 11) || (access_type > ACCESS_TYPE_MULTICAST)) {
+		DEBUG_TRAINING_IP_ENGINE(
+			DEBUG_LEVEL_ERROR,
+			("wrong parameter train_cs_type %d edge_comp %d pattern %d direction %d search_dir %d control_element %d result_type %d pup_num %d pup_access_type %d if_id %d access_type %d\n",
+			 train_cs_type, edge_comp, pattern, direction,
+			 search_dir, control_element, result_type, pup_num,
+			 pup_access_type, if_id, access_type));
+		return MV_FAIL;
+	}
+
+	if (edge_comp == EDGE_FPF) {
+		start_search = HWS_LOW2HIGH;
+		end_search = HWS_HIGH2LOW;
+		edge_comp_used = EDGE_FP;
+	} else {
+		start_search = search_dir;
+		end_search = search_dir;
+		edge_comp_used = edge_comp;
+	}
+
+	for (search_dir_id = start_search; search_dir_id <= end_search;
+	     search_dir_id++) {
+		init_value_used = (search_dir_id == HWS_LOW2HIGH) ?
+			init_value_l2h : init_value_h2l;
+		DEBUG_TRAINING_IP_ENGINE(
+			DEBUG_LEVEL_TRACE,
+			("dev_num %d, access_type %d, if_id %d, pup_access_type %d,pup_num %d, result_type %d, control_element %d search_dir_id %d, direction %d, interface_mask %d,init_value_used %d, num_iter %d, pattern %d, edge_comp_used %d, train_cs_type %d, cs_num %d\n",
+			 dev_num, access_type, if_id, pup_access_type, pup_num,
+			 result_type, control_element, search_dir_id,
+			 direction, interface_mask, init_value_used, num_iter,
+			 pattern, edge_comp_used, train_cs_type, cs_num));
+
+		ddr3_tip_ip_training(dev_num, access_type, if_id,
+				     pup_access_type, pup_num, result_type,
+				     control_element, search_dir_id, direction,
+				     interface_mask, init_value_used, num_iter,
+				     pattern, edge_comp_used, train_cs_type,
+				     cs_num, train_status);
+		if (access_type == ACCESS_TYPE_MULTICAST) {
+			start_if = 0;
+			end_if = MAX_INTERFACE_NUM - 1;
+		} else {
+			start_if = if_id;
+			end_if = if_id;
+		}
+
+		for (interface_num = start_if; interface_num <= end_if;
+		     interface_num++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, interface_num);
+			cs_num = 0;
+			CHECK_STATUS(ddr3_tip_read_training_result
+				     (dev_num, interface_num, pup_access_type,
+				      pup_num, bit_num, search_dir_id,
+				      direction, result_type,
+				      TRAINING_LOAD_OPERATION_UNLOAD,
+				      train_cs_type, NULL, 0, cons_tap,
+				      0));
+		}
+	}
+
+	return MV_OK;
+}
+/*
+ * Training search & read result routine
+ * This function implements the search algorithm
+ * first it calls the function ddr3_tip_ip_training_wrapper_int which triggers the search from l2h and h2l
+ * this function handles rx and tx search cases
+ * in case of rx it only triggers the search (l2h and h2l)
+ * in case of tx there are 3 optional algorithm phases:
+ * phase 1:
+ * it first triggers the search and handles the results as following (phase 1):
+ * each bit, which defined by the search two edges (e1 or VW_L and e2 or VW_H), match on of cases:
+ *  1.	BIT_LOW_UI	0 =< VW =< 31 in case of jitter use: VW_L <= 31, VW_H <= 31
+ *  2.	BIT_HIGH_UI	32 =< VW =< 63 in case of jitter use: VW_L >= 32, VW_H >= 32
+ *  3.	BIT_SPLIT_IN	VW_L <= 31 & VW_H >= 32
+ *  4.	BIT_SPLIT_OUT*	VW_H < 32 &  VW_L > 32
+ * note: the VW units is adll taps
+ * phase 2:
+ * only bit case BIT_SPLIT_OUT requires another search (phase 2) from the middle range in two directions h2l and l2h
+ * because only this case is not locked by the search engine in the first search trigger (phase 1).
+ * phase 3:
+ * each subphy is categorized according to its bits definition.
+ * the sub-phy cases are as follows:
+ *  1.BYTE_NOT_DEFINED			the byte has not yet been categorized
+ *  2.BYTE_HOMOGENEOUS_LOW		0 =< VW =< 31
+ *  3.BYTE_HOMOGENEOUS_HIGH		32 =< VW =< 63
+ *  4.BYTE_HOMOGENEOUS_SPLIT_IN		VW_L <= 31 & VW_H >= 32
+ *					or the center of all bits in the byte  =< 31
+ *  5.BYTE_HOMOGENEOUS_SPLIT_OUT	VW_H < 32 &  VW_L > 32
+ *  6.BYTE_SPLIT_OUT_MIX		at least one bits is in split out state and one bit is in other
+ *					or the center of all bits in the byte => 32
+ * after the two phases above a center valid window for each subphy is calculated accordingly:
+ * center valid window = maximum center of all bits in the subphy - minimum center of all bits in the subphy.
+ * now decisions are made in each subphy as following:
+ * all subphys which are homogeneous remains as is
+ * all subphys which are homogeneous low | homogeneous high and the subphy center valid window is less than 32
+ *	mark this subphy as homogeneous split in.
+ * now the bits in the bytes which are BYTE_SPLIT_OUT_MIX needed to be reorganized and handles as following
+ * all bits which are BIT_LOW_UI will be added with 64 adll,
+ * this will hopefully ensures that all the bits in the sub phy can be sampled by the dqs
+ */
+int ddr3_tip_ip_training_wrapper(u32 dev_num, enum hws_access_type access_type,
+	u32 if_id,
+	enum hws_access_type pup_access_type,
+	u32 pup_num,
+	enum hws_training_result result_type,
+	enum hws_control_element control_element,
+	enum hws_search_dir search_dir,
+	enum hws_dir direction, u32 interface_mask,
+	u32 init_value_l2h, u32 init_value_h2l,
+	u32 num_iter, enum hws_pattern pattern,
+	enum hws_edge_compare edge_comp,
+	enum hws_ddr_cs train_cs_type, u32 cs_num,
+	enum hws_training_ip_stat *train_status)
+{
+	u8 e1, e2;
+	u32 bit_id, start_if, end_if, bit_end = 0;
+	u32 *result[HWS_SEARCH_DIR_LIMIT] = { 0 };
+	u8 cons_tap = (direction == OPER_WRITE) ? (64) : (0);
+	u8 bit_bit_mask[MAX_BUS_NUM] = { 0 }, bit_bit_mask_active = 0;
+	u8 bit_state[MAX_BUS_NUM * BUS_WIDTH_IN_BITS] = {0};
+	u8 h2l_adll_value[MAX_BUS_NUM][BUS_WIDTH_IN_BITS];
+	u8 l2h_adll_value[MAX_BUS_NUM][BUS_WIDTH_IN_BITS];
+	u8 center_subphy_adll_window[MAX_BUS_NUM];
+	u8 min_center_subphy_adll[MAX_BUS_NUM];
+	u8 max_center_subphy_adll[MAX_BUS_NUM];
+	u32 *l2h_if_train_res = NULL;
+	u32 *h2l_if_train_res = NULL;
+	enum hws_search_dir search_dir_id;
+	int status;
+	u32 bit_lock_result;
+
+	u8 sybphy_id;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (pup_num >= octets_per_if_num) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+			("pup_num %d not valid\n", pup_num));
+	}
+
+	if (if_id >= MAX_INTERFACE_NUM) {
+		DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
+			("if_id %d not valid\n", if_id));
+	}
+
+	status = ddr3_tip_ip_training_wrapper_int
+		(dev_num, access_type, if_id, pup_access_type, pup_num,
+		ALL_BITS_PER_PUP, result_type, control_element,
+		search_dir, direction, interface_mask, init_value_l2h,
+		init_value_h2l, num_iter, pattern, edge_comp,
+		train_cs_type, cs_num, train_status);
+
+	if (MV_OK != status)
+		return status;
+
+	if (access_type == ACCESS_TYPE_MULTICAST) {
+		start_if = 0;
+		end_if = MAX_INTERFACE_NUM - 1;
+	} else {
+		start_if = if_id;
+		end_if = if_id;
+	}
+
+	for (if_id = start_if; if_id <= end_if; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* zero the database */
+		bit_bit_mask_active = 0;	/* clean the flag for level2 search */
+		memset(bit_state, 0, sizeof(bit_state));
+		/* phase 1 */
+		for (sybphy_id = 0; sybphy_id < octets_per_if_num; sybphy_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sybphy_id);
+			if (result_type == RESULT_PER_BIT)
+				bit_end = BUS_WIDTH_IN_BITS;
+			else
+				bit_end = 0;
+
+			/* zero the data base */
+			bit_bit_mask[sybphy_id] = 0;
+			byte_status[if_id][sybphy_id] = BYTE_NOT_DEFINED;
+			for (bit_id = 0; bit_id < bit_end; bit_id++) {
+				h2l_adll_value[sybphy_id][bit_id] = 64;
+				l2h_adll_value[sybphy_id][bit_id] = 0;
+				for (search_dir_id = HWS_LOW2HIGH; search_dir_id <= HWS_HIGH2LOW;
+					search_dir_id++) {
+					status = ddr3_tip_read_training_result
+						(dev_num, if_id,
+							ACCESS_TYPE_UNICAST, sybphy_id, bit_id,
+							search_dir_id, direction, result_type,
+							TRAINING_LOAD_OPERATION_UNLOAD, CS_SINGLE,
+							&result[search_dir_id], 1, 0, 0);
+
+					if (MV_OK != status)
+						return status;
+				}
+
+				e1 = GET_TAP_RESULT(result[HWS_LOW2HIGH][0], EDGE_1);
+				e2 = GET_TAP_RESULT(result[HWS_HIGH2LOW][0], EDGE_1);
+				DEBUG_TRAINING_IP_ENGINE
+					(DEBUG_LEVEL_INFO,
+					 ("if_id %d sybphy_id %d bit %d l2h 0x%x (e1 0x%x) h2l 0x%x (e2 0x%x)\n",
+					 if_id, sybphy_id, bit_id, result[HWS_LOW2HIGH][0], e1,
+					 result[HWS_HIGH2LOW][0], e2));
+				bit_lock_result =
+					(GET_LOCK_RESULT(result[HWS_LOW2HIGH][0]) &&
+						GET_LOCK_RESULT(result[HWS_HIGH2LOW][0]));
+
+				if (bit_lock_result) {
+					/* in case of read operation set the byte status as homogeneous low */
+					if (direction == OPER_READ) {
+						byte_status[if_id][sybphy_id] |= BYTE_HOMOGENEOUS_LOW;
+					} else if ((e2 - e1) > 32) { /* oper_write */
+						/* split out */
+						bit_state[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] =
+							BIT_SPLIT_OUT;
+						byte_status[if_id][sybphy_id] |= BYTE_HOMOGENEOUS_SPLIT_OUT;
+						/* mark problem bits */
+						bit_bit_mask[sybphy_id] |= (1 << bit_id);
+						bit_bit_mask_active = 1;
+						DEBUG_TRAINING_IP_ENGINE
+							(DEBUG_LEVEL_TRACE,
+							 ("if_id %d sybphy_id %d bit %d BIT_SPLIT_OUT\n",
+							 if_id, sybphy_id, bit_id));
+					} else {
+						/* low ui */
+						if (e1 <= 31 && e2 <= 31) {
+							bit_state[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] =
+								BIT_LOW_UI;
+							byte_status[if_id][sybphy_id] |= BYTE_HOMOGENEOUS_LOW;
+							l2h_adll_value[sybphy_id][bit_id] = e1;
+							h2l_adll_value[sybphy_id][bit_id] = e2;
+							DEBUG_TRAINING_IP_ENGINE
+								(DEBUG_LEVEL_TRACE,
+								 ("if_id %d sybphy_id %d bit %d BIT_LOW_UI\n",
+								 if_id, sybphy_id, bit_id));
+						}
+							/* high ui */
+						if (e1 >= 32 && e2 >= 32) {
+							bit_state[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] =
+								BIT_HIGH_UI;
+							byte_status[if_id][sybphy_id] |= BYTE_HOMOGENEOUS_HIGH;
+							l2h_adll_value[sybphy_id][bit_id] = e1;
+							h2l_adll_value[sybphy_id][bit_id] = e2;
+							DEBUG_TRAINING_IP_ENGINE
+								(DEBUG_LEVEL_TRACE,
+								 ("if_id %d sybphy_id %d bit %d BIT_HIGH_UI\n",
+								 if_id, sybphy_id, bit_id));
+						}
+						/* split in */
+						if (e1 <= 31 && e2 >= 32) {
+							bit_state[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] =
+								BIT_SPLIT_IN;
+							byte_status[if_id][sybphy_id] |=
+								BYTE_HOMOGENEOUS_SPLIT_IN;
+							l2h_adll_value[sybphy_id][bit_id] = e1;
+							h2l_adll_value[sybphy_id][bit_id] = e2;
+							DEBUG_TRAINING_IP_ENGINE
+								(DEBUG_LEVEL_TRACE,
+								 ("if_id %d sybphy_id %d bit %d BIT_SPLIT_IN\n",
+								 if_id, sybphy_id, bit_id));
+						}
+					}
+				} else {
+					DEBUG_TRAINING_IP_ENGINE
+						(DEBUG_LEVEL_INFO,
+						 ("if_id %d sybphy_id %d bit %d l2h 0x%x (e1 0x%x)"
+						 "h2l 0x%x (e2 0x%x): bit cannot be categorized\n",
+						 if_id, sybphy_id, bit_id, result[HWS_LOW2HIGH][0], e1,
+						 result[HWS_HIGH2LOW][0], e2));
+					/* mark the byte as not defined */
+					byte_status[if_id][sybphy_id] = BYTE_NOT_DEFINED;
+					break; /* continue to next pup - no reason to analyze this byte */
+				}
+			} /* for all bits */
+		} /* for all PUPs */
+
+		/* phase 2 will occur only in write operation */
+		if (bit_bit_mask_active != 0) {
+			l2h_if_train_res = ddr3_tip_get_buf_ptr(dev_num, HWS_LOW2HIGH, result_type, if_id);
+			h2l_if_train_res = ddr3_tip_get_buf_ptr(dev_num, HWS_HIGH2LOW, result_type, if_id);
+			/* search from middle to end */
+			ddr3_tip_ip_training
+				(dev_num, ACCESS_TYPE_UNICAST,
+				 if_id, ACCESS_TYPE_MULTICAST,
+				 PARAM_NOT_CARE, result_type,
+				 control_element, HWS_LOW2HIGH,
+				 direction, interface_mask,
+				 num_iter / 2, num_iter / 2,
+				 pattern, EDGE_FP, train_cs_type,
+				 cs_num, train_status);
+
+			for (sybphy_id = 0; sybphy_id < octets_per_if_num; sybphy_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sybphy_id);
+				if (byte_status[if_id][sybphy_id] != BYTE_NOT_DEFINED) {
+					if (bit_bit_mask[sybphy_id] == 0)
+						continue; /* this byte bits have no split out state */
+
+					for (bit_id = 0; bit_id < bit_end; bit_id++) {
+						if ((bit_bit_mask[sybphy_id] & (1 << bit_id)) == 0)
+							continue; /* this bit is non split goto next bit */
+
+						/* enter the result to the data base */
+						status = ddr3_tip_read_training_result
+							(dev_num, if_id, ACCESS_TYPE_UNICAST, sybphy_id,
+							 bit_id, HWS_LOW2HIGH, direction, result_type,
+							 TRAINING_LOAD_OPERATION_UNLOAD, CS_SINGLE,
+							 &l2h_if_train_res, 0, 0, 1);
+
+						if (MV_OK != status)
+							return status;
+
+						l2h_adll_value[sybphy_id][bit_id] =
+							l2h_if_train_res[sybphy_id *
+							BUS_WIDTH_IN_BITS + bit_id] & PUP_RESULT_EDGE_1_MASK;
+					}
+				}
+			}
+			/* Search from middle to start */
+			ddr3_tip_ip_training
+				(dev_num, ACCESS_TYPE_UNICAST,
+				 if_id, ACCESS_TYPE_MULTICAST,
+				 PARAM_NOT_CARE, result_type,
+				 control_element, HWS_HIGH2LOW,
+				 direction, interface_mask,
+				 num_iter / 2, num_iter / 2,
+				 pattern, EDGE_FP, train_cs_type,
+				 cs_num, train_status);
+
+			for (sybphy_id = 0; sybphy_id < octets_per_if_num; sybphy_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sybphy_id);
+				if (byte_status[if_id][sybphy_id] != BYTE_NOT_DEFINED) {
+					if (bit_bit_mask[sybphy_id] == 0)
+						continue;
+
+					for (bit_id = 0; bit_id < bit_end; bit_id++) {
+						if ((bit_bit_mask[sybphy_id] & (1 << bit_id)) == 0)
+							continue;
+
+						status = ddr3_tip_read_training_result
+							(dev_num, if_id, ACCESS_TYPE_UNICAST, sybphy_id,
+							 bit_id, HWS_HIGH2LOW, direction, result_type,
+							 TRAINING_LOAD_OPERATION_UNLOAD, CS_SINGLE,
+							 &h2l_if_train_res, 0, cons_tap, 1);
+
+						if (MV_OK != status)
+							return status;
+
+						h2l_adll_value[sybphy_id][bit_id] =
+							h2l_if_train_res[sybphy_id *
+							BUS_WIDTH_IN_BITS + bit_id] & PUP_RESULT_EDGE_1_MASK;
+					}
+				}
+			}
+		} /* end if bit_bit_mask_active */
+		/*
+			* phase 3 will occur only in write operation
+			* find the maximum and the minimum center of each subphy
+			*/
+		for (sybphy_id = 0; sybphy_id < octets_per_if_num; sybphy_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sybphy_id);
+
+			if ((byte_status[if_id][sybphy_id] != BYTE_NOT_DEFINED) && (direction == OPER_WRITE)) {
+				/* clear the arrays and parameters */
+				center_subphy_adll_window[sybphy_id] = 0;
+				max_center_subphy_adll[sybphy_id] = 0;
+				min_center_subphy_adll[sybphy_id] = 64;
+				/* find the max and min center adll value in the current subphy */
+				for (bit_id = 0; bit_id < bit_end; bit_id++) {
+					/* debug print all the bit edges after alignment */
+					DEBUG_TRAINING_IP_ENGINE
+						(DEBUG_LEVEL_TRACE,
+						 ("if_id %d sybphy_id %d bit %d l2h %d h2l %d\n",
+						 if_id, sybphy_id, bit_id, l2h_adll_value[sybphy_id][bit_id],
+						 h2l_adll_value[sybphy_id][bit_id]));
+
+					if (((l2h_adll_value[sybphy_id][bit_id] +
+					      h2l_adll_value[sybphy_id][bit_id]) / 2) >
+					      max_center_subphy_adll[sybphy_id])
+						max_center_subphy_adll[sybphy_id] =
+						(l2h_adll_value[sybphy_id][bit_id] +
+						 h2l_adll_value[sybphy_id][bit_id]) / 2;
+					if (((l2h_adll_value[sybphy_id][bit_id] +
+					      h2l_adll_value[sybphy_id][bit_id]) / 2) <
+					      min_center_subphy_adll[sybphy_id])
+						min_center_subphy_adll[sybphy_id] =
+						(l2h_adll_value[sybphy_id][bit_id] +
+						 h2l_adll_value[sybphy_id][bit_id]) / 2;
+				}
+
+				/* calculate the center of the current subphy */
+				center_subphy_adll_window[sybphy_id] =
+					max_center_subphy_adll[sybphy_id] -
+					min_center_subphy_adll[sybphy_id];
+				DEBUG_TRAINING_IP_ENGINE
+					(DEBUG_LEVEL_TRACE,
+					 ("if_id %d sybphy_id %d min center %d max center %d center %d\n",
+					 if_id, sybphy_id, min_center_subphy_adll[sybphy_id],
+					 max_center_subphy_adll[sybphy_id],
+					 center_subphy_adll_window[sybphy_id]));
+			}
+		}
+		/*
+			* check byte state and fix bits state if needed
+			* in case the level 1 and 2 above subphy results are
+			* homogeneous continue to the next subphy
+			*/
+		for (sybphy_id = 0; sybphy_id < octets_per_if_num; sybphy_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sybphy_id);
+			if ((byte_status[if_id][sybphy_id] == BYTE_HOMOGENEOUS_LOW) ||
+			    (byte_status[if_id][sybphy_id] == BYTE_HOMOGENEOUS_HIGH) ||
+			    (byte_status[if_id][sybphy_id] == BYTE_HOMOGENEOUS_SPLIT_IN) ||
+			    (byte_status[if_id][sybphy_id] == BYTE_HOMOGENEOUS_SPLIT_OUT) ||
+			    (byte_status[if_id][sybphy_id] == BYTE_NOT_DEFINED))
+			continue;
+
+			/*
+			 * in case all of the bits in the current subphy are
+			 * less than 32 which will find alignment in the subphy bits
+			 * mark this subphy as homogeneous split in
+			*/
+			if (center_subphy_adll_window[sybphy_id] <= 31)
+				byte_status[if_id][sybphy_id] = BYTE_HOMOGENEOUS_SPLIT_IN;
+
+			/*
+				* in case the current byte is split_out and the center is bigger than 31
+				* the byte can be aligned. in this case add 64 to the the low ui bits aligning it
+				* to the other ui bits
+				*/
+			if (center_subphy_adll_window[sybphy_id] >= 32) {
+				byte_status[if_id][sybphy_id] = BYTE_SPLIT_OUT_MIX;
+
+				DEBUG_TRAINING_IP_ENGINE
+					(DEBUG_LEVEL_TRACE,
+					 ("if_id %d sybphy_id %d byte state 0x%x\n",
+					 if_id, sybphy_id, byte_status[if_id][sybphy_id]));
+				for (bit_id = 0; bit_id < bit_end; bit_id++) {
+					if (bit_state[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] == BIT_LOW_UI) {
+						l2h_if_train_res[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] += 64;
+						h2l_if_train_res[sybphy_id * BUS_WIDTH_IN_BITS + bit_id] += 64;
+					}
+					DEBUG_TRAINING_IP_ENGINE
+						(DEBUG_LEVEL_TRACE,
+						 ("if_id %d sybphy_id %d bit_id %d added 64 adlls\n",
+						 if_id, sybphy_id, bit_id));
+				}
+			}
+		}
+	} /* for all interfaces */
+
+	return MV_OK;
+}
+
+u8 mv_ddr_tip_sub_phy_byte_status_get(u32 if_id, u32 subphy_id)
+{
+	return byte_status[if_id][subphy_id];
+}
+
+void mv_ddr_tip_sub_phy_byte_status_set(u32 if_id, u32 subphy_id, u8 byte_status_data)
+{
+	byte_status[if_id][subphy_id] = byte_status_data;
+}
+
+/*
+ * Load phy values
+ */
+int ddr3_tip_load_phy_values(int b_load)
+{
+	u32 bus_cnt = 0, if_id, dev_num = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_cnt = 0; bus_cnt < octets_per_if_num; bus_cnt++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+			if (b_load == 1) {
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, bus_cnt,
+					      DDR_PHY_DATA,
+					      CTX_PHY_REG(effective_cs),
+					      &phy_reg_bk[if_id][bus_cnt]
+					      [0]));
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, bus_cnt,
+					      DDR_PHY_DATA,
+					      RL_PHY_REG(effective_cs),
+					      &phy_reg_bk[if_id][bus_cnt]
+					      [1]));
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, bus_cnt,
+					      DDR_PHY_DATA,
+					      CRX_PHY_REG(effective_cs),
+					      &phy_reg_bk[if_id][bus_cnt]
+					      [2]));
+			} else {
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      bus_cnt, DDR_PHY_DATA,
+					      CTX_PHY_REG(effective_cs),
+					      phy_reg_bk[if_id][bus_cnt]
+					      [0]));
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      bus_cnt, DDR_PHY_DATA,
+					      RL_PHY_REG(effective_cs),
+					      phy_reg_bk[if_id][bus_cnt]
+					      [1]));
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST,
+					      bus_cnt, DDR_PHY_DATA,
+					      CRX_PHY_REG(effective_cs),
+					      phy_reg_bk[if_id][bus_cnt]
+					      [2]));
+			}
+		}
+	}
+
+	return MV_OK;
+}
+
+int ddr3_tip_training_ip_test(u32 dev_num, enum hws_training_result result_type,
+			      enum hws_search_dir search_dir,
+			      enum hws_dir direction,
+			      enum hws_edge_compare edge,
+			      u32 init_val1, u32 init_val2,
+			      u32 num_of_iterations,
+			      u32 start_pattern, u32 end_pattern)
+{
+	u32 pattern, if_id, pup_id;
+	enum hws_training_ip_stat train_status[MAX_INTERFACE_NUM];
+	u32 *res = NULL;
+	u32 search_state = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	ddr3_tip_load_phy_values(1);
+
+	for (pattern = start_pattern; pattern <= end_pattern; pattern++) {
+		for (search_state = 0; search_state < HWS_SEARCH_DIR_LIMIT;
+		     search_state++) {
+			ddr3_tip_ip_training_wrapper(dev_num,
+						     ACCESS_TYPE_MULTICAST, 0,
+						     ACCESS_TYPE_MULTICAST, 0,
+						     result_type,
+						     HWS_CONTROL_ELEMENT_ADLL,
+						     search_dir, direction,
+						     0xfff, init_val1,
+						     init_val2,
+						     num_of_iterations, pattern,
+						     edge, CS_SINGLE,
+						     PARAM_NOT_CARE,
+						     train_status);
+
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				for (pup_id = 0; pup_id <
+					     octets_per_if_num;
+				     pup_id++) {
+					VALIDATE_BUS_ACTIVE(tm->bus_act_mask,
+							pup_id);
+					CHECK_STATUS
+						(ddr3_tip_read_training_result
+						 (dev_num, if_id,
+						  ACCESS_TYPE_UNICAST, pup_id,
+						  ALL_BITS_PER_PUP,
+						  search_state,
+						  direction, result_type,
+						  TRAINING_LOAD_OPERATION_UNLOAD,
+						  CS_SINGLE, &res, 1, 0,
+						  0));
+					if (result_type == RESULT_PER_BYTE) {
+						DEBUG_TRAINING_IP_ENGINE
+							(DEBUG_LEVEL_INFO,
+							 ("search_state %d if_id %d pup_id %d 0x%x\n",
+							  search_state, if_id,
+							  pup_id, res[0]));
+					} else {
+						DEBUG_TRAINING_IP_ENGINE
+							(DEBUG_LEVEL_INFO,
+							 ("search_state %d if_id %d pup_id %d 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+							  search_state, if_id,
+							  pup_id, res[0],
+							  res[1], res[2],
+							  res[3], res[4],
+							  res[5], res[6],
+							  res[7]));
+					}
+				}
+			}	/* interface */
+		}		/* search */
+	}			/* pattern */
+
+	ddr3_tip_load_phy_values(0);
+
+	return MV_OK;
+}
+
+int mv_ddr_pattern_start_addr_set(struct pattern_info *pattern_tbl, enum hws_pattern pattern, u32 addr)
+{
+	pattern_tbl[pattern].start_addr = addr;
+
+	return 0;
+}
+
+struct pattern_info *ddr3_tip_get_pattern_table()
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask))
+		return pattern_table_64;
+	else if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) == 0)
+		return pattern_table_32;
+	else
+		return pattern_table_16;
+}
+
+u16 *ddr3_tip_get_mask_results_dq_reg()
+{
+#if MAX_BUS_NUM == 5
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))
+		return mask_results_dq_reg_map_pup3_ecc;
+	else
+#endif
+		return mask_results_dq_reg_map;
+}
+
+u16 *ddr3_tip_get_mask_results_pup_reg_map()
+{
+#if MAX_BUS_NUM == 5
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))
+		return mask_results_pup_reg_map_pup3_ecc;
+	else
+#endif
+		return mask_results_pup_reg_map;
+}
+
+/* load expected dm pattern to odpg */
+#define LOW_NIBBLE_BYTE_MASK	0xf
+#define HIGH_NIBBLE_BYTE_MASK	0xf0
+int mv_ddr_load_dm_pattern_to_odpg(enum hws_access_type access_type, enum hws_pattern pattern,
+				   enum dm_direction dm_dir)
+{
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	u32 pattern_len = 0;
+	u32 data_low, data_high;
+	u8 dm_data;
+
+	for (pattern_len = 0;
+	     pattern_len < pattern_table[pattern].pattern_len;
+	     pattern_len++) {
+		if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask)) {
+			data_low = pattern_table_get_word(0, pattern, (u8)pattern_len);
+			data_high = data_low;
+		} else {
+			data_low = pattern_table_get_word(0, pattern, (u8)(pattern_len * 2));
+			data_high = pattern_table_get_word(0, pattern, (u8)(pattern_len * 2 + 1));
+		}
+
+		/* odpg mbus dm definition is opposite to ddr4 protocol */
+		if (dm_dir == DM_DIR_INVERSE)
+			dm_data = ~((data_low & LOW_NIBBLE_BYTE_MASK) | (data_high & HIGH_NIBBLE_BYTE_MASK));
+		else
+			dm_data = (data_low & LOW_NIBBLE_BYTE_MASK) | (data_high & HIGH_NIBBLE_BYTE_MASK);
+
+		ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_WR_DATA_LOW_REG, data_low, MASK_ALL_BITS);
+		ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_WR_DATA_HIGH_REG, data_high, MASK_ALL_BITS);
+		ddr3_tip_if_write(0, access_type, 0, ODPG_DATA_WR_ADDR_REG,
+				  pattern_len | ((dm_data & ODPG_DATA_WR_DATA_MASK) << ODPG_DATA_WR_DATA_OFFS),
+				  MASK_ALL_BITS);
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.h
new file mode 100644
index 0000000..2d40e68
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_ENGINE_H_
+#define _DDR3_TRAINING_IP_ENGINE_H_
+
+#include "ddr3_training_ip_def.h"
+#include "ddr3_training_ip_flow.h"
+#include "ddr3_training_ip_pbs.h"
+
+#define EDGE_1				0
+#define EDGE_2				1
+#define ALL_PUP_TRAINING		0xe
+#define PUP_RESULT_EDGE_1_MASK		0xff
+#define PUP_RESULT_EDGE_2_MASK		(0xff << 8)
+#define PUP_LOCK_RESULT_BIT		25
+
+#define GET_TAP_RESULT(reg, edge)				 \
+	(((edge) == EDGE_1) ? ((reg) & PUP_RESULT_EDGE_1_MASK) : \
+	 (((reg) & PUP_RESULT_EDGE_2_MASK) >> 8));
+#define GET_LOCK_RESULT(reg)						\
+	(((reg) & (1<<PUP_LOCK_RESULT_BIT)) >> PUP_LOCK_RESULT_BIT)
+
+#define EDGE_FAILURE			128
+#define ALL_BITS_PER_PUP		128
+
+#define MIN_WINDOW_SIZE			6
+#define MAX_WINDOW_SIZE_RX		32
+#define MAX_WINDOW_SIZE_TX		64
+
+int ddr3_tip_training_ip_test(u32 dev_num, enum hws_training_result result_type,
+			      enum hws_search_dir search_dir,
+			      enum hws_dir direction,
+			      enum hws_edge_compare edge,
+			      u32 init_val1, u32 init_val2,
+			      u32 num_of_iterations, u32 start_pattern,
+			      u32 end_pattern);
+int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern pattern);
+int ddr3_tip_load_all_pattern_to_mem(u32 dev_num);
+int ddr3_tip_read_training_result(u32 dev_num, u32 if_id,
+				  enum hws_access_type pup_access_type,
+				  u32 pup_num, u32 bit_num,
+				  enum hws_search_dir search,
+				  enum hws_dir direction,
+				  enum hws_training_result result_type,
+				  enum hws_training_load_op operation,
+				  u32 cs_num_type, u32 **load_res,
+				  int is_read_from_db, u8 cons_tap,
+				  int is_check_result_validity);
+int ddr3_tip_ip_training(u32 dev_num, enum hws_access_type access_type,
+			 u32 interface_num,
+			 enum hws_access_type pup_access_type,
+			 u32 pup_num, enum hws_training_result result_type,
+			 enum hws_control_element control_element,
+			 enum hws_search_dir search_dir, enum hws_dir direction,
+			 u32 interface_mask, u32 init_value, u32 num_iter,
+			 enum hws_pattern pattern,
+			 enum hws_edge_compare edge_comp,
+			 enum hws_ddr_cs cs_type, u32 cs_num,
+			 enum hws_training_ip_stat *train_status);
+int ddr3_tip_ip_training_wrapper(u32 dev_num, enum hws_access_type access_type,
+				 u32 if_id,
+				 enum hws_access_type pup_access_type,
+				 u32 pup_num,
+				 enum hws_training_result result_type,
+				 enum hws_control_element control_element,
+				 enum hws_search_dir search_dir,
+				 enum hws_dir direction,
+				 u32 interface_mask, u32 init_value1,
+				 u32 init_value2, u32 num_iter,
+				 enum hws_pattern pattern,
+				 enum hws_edge_compare edge_comp,
+				 enum hws_ddr_cs train_cs_type, u32 cs_num,
+				 enum hws_training_ip_stat *train_status);
+u8 mv_ddr_tip_sub_phy_byte_status_get(u32 if_id, u32 subphy_id);
+void mv_ddr_tip_sub_phy_byte_status_set(u32 if_id, u32 subphy_id, u8 byte_status_data);
+void ddr3_tip_print_bist_res(void);
+struct pattern_info *ddr3_tip_get_pattern_table(void);
+u16 *ddr3_tip_get_mask_results_dq_reg(void);
+u16 *ddr3_tip_get_mask_results_pup_reg_map(void);
+int mv_ddr_load_dm_pattern_to_odpg(enum hws_access_type access_type, enum hws_pattern pattern,
+				   enum dm_direction dm_dir);
+int mv_ddr_pattern_start_addr_set(struct pattern_info *pattern_tbl, enum hws_pattern pattern, u32 addr);
+#endif /* _DDR3_TRAINING_IP_ENGINE_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_flow.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_flow.h
new file mode 100644
index 0000000..ab152cb
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_flow.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_FLOW_H_
+#define _DDR3_TRAINING_IP_FLOW_H_
+
+#include "ddr3_training_ip.h"
+#include "ddr3_training_ip_db.h"
+
+#define KILLER_PATTERN_LENGTH		32
+#define EXT_ACCESS_BURST_LENGTH		8
+
+#define ECC_READ_BUS_0			0
+#define ECC_PHY_ACCESS_3		3
+#define ECC_PHY_ACCESS_4		4
+#define ECC_PHY_ACCESS_8		8
+#define BUS_WIDTH_IN_BITS		8
+#define MAX_POLLING_ITERATIONS		1000000
+#define ADLL_LENGTH			32
+
+#define GP_RSVD0_REG			0x182e0
+
+/*
+ * DFX address Space
+ * Table 2: DFX address space
+ * Address Bits   Value   Description
+ * [31 : 20]   0x? DFX base address bases PCIe mapping
+ * [19 : 15]   0...Number_of_client-1   Client Index inside pipe.
+ *             See also Table 1 Multi_cast = 29 Broadcast = 28
+ * [14 : 13]   2'b01   Access to Client Internal Register
+ * [12 : 0]   Client Internal Register offset   See related Client Registers
+ * [14 : 13]   2'b00   Access to Ram Wrappers Internal Register
+ * [12 : 6]   0 Number_of_rams-1   Ram Index inside Client
+ * [5 : 0]   Ram Wrapper Internal Register offset   See related Ram Wrappers
+ * Registers
+ */
+
+/* nsec */
+#define AUTO_ZQC_TIMING				15384
+
+enum mr_number {
+	MR_CMD0,
+	MR_CMD1,
+	MR_CMD2,
+	MR_CMD3,
+	MR_LAST
+};
+
+struct mv_ddr_mr_data {
+	u32 cmd;
+	u32 reg_addr;
+};
+
+struct write_supp_result {
+	enum hws_wl_supp stage;
+	int is_pup_fail;
+};
+
+int ddr3_tip_write_leveling_static_config(u32 dev_num, u32 if_id,
+					  enum mv_ddr_freq frequency,
+					  u32 *round_trip_delay_arr);
+int ddr3_tip_read_leveling_static_config(u32 dev_num, u32 if_id,
+					 enum mv_ddr_freq frequency,
+					 u32 *total_round_trip_delay_arr);
+int ddr3_tip_if_write(u32 dev_num, enum hws_access_type interface_access,
+		      u32 if_id, u32 reg_addr, u32 data_value, u32 mask);
+int ddr3_tip_if_polling(u32 dev_num, enum hws_access_type access_type,
+			u32 if_id, u32 exp_value, u32 mask, u32 offset,
+			u32 poll_tries);
+int ddr3_tip_if_read(u32 dev_num, enum hws_access_type interface_access,
+		     u32 if_id, u32 reg_addr, u32 *data, u32 mask);
+int ddr3_tip_bus_read_modify_write(u32 dev_num,
+				   enum hws_access_type access_type,
+				   u32 if_id, u32 phy_id,
+				   enum hws_ddr_phy phy_type,
+				   u32 reg_addr, u32 data_value, u32 reg_mask);
+int ddr3_tip_bus_read(u32 dev_num, u32 if_id, enum hws_access_type phy_access,
+		      u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr,
+		      u32 *data);
+int ddr3_tip_bus_write(u32 dev_num, enum hws_access_type e_interface_access,
+		       u32 if_id, enum hws_access_type e_phy_access, u32 phy_id,
+		       enum hws_ddr_phy e_phy_type, u32 reg_addr,
+		       u32 data_value);
+int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type e_access, u32 if_id,
+		      enum mv_ddr_freq memory_freq);
+int ddr3_tip_adjust_dqs(u32 dev_num);
+int ddr3_tip_init_controller(u32 dev_num);
+int ddr3_tip_ext_read(u32 dev_num, u32 if_id, u32 reg_addr,
+		      u32 num_of_bursts, u32 *addr);
+int ddr3_tip_ext_write(u32 dev_num, u32 if_id, u32 reg_addr,
+		       u32 num_of_bursts, u32 *addr);
+int ddr3_tip_dynamic_read_leveling(u32 dev_num, u32 ui_freq);
+int mv_ddr_rl_dqs_burst(u32 dev_num, u32 if_id, u32 freq);
+int ddr3_tip_legacy_dynamic_read_leveling(u32 dev_num);
+int ddr3_tip_dynamic_per_bit_read_leveling(u32 dev_num, u32 ui_freq);
+int ddr3_tip_legacy_dynamic_write_leveling(u32 dev_num);
+int ddr3_tip_dynamic_write_leveling(u32 dev_num, int phase_remove);
+int ddr3_tip_dynamic_write_leveling_supp(u32 dev_num);
+int ddr3_tip_static_init_controller(u32 dev_num);
+int ddr3_tip_configure_phy(u32 dev_num);
+int ddr3_tip_load_pattern_to_odpg(u32 dev_num, enum hws_access_type access_type,
+				  u32 if_id, enum hws_pattern pattern,
+				  u32 load_addr);
+int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern e_pattern);
+int ddr3_tip_configure_odpg(u32 dev_num, enum hws_access_type access_type,
+			    u32 if_id, enum hws_dir direction, u32 tx_phases,
+			    u32 tx_burst_size, u32 rx_phases,
+			    u32 delay_between_burst, u32 rd_mode, u32 cs_num,
+			    u32 addr_stress_jump, u32 single_pattern);
+int ddr3_tip_write_mrs_cmd(u32 dev_num, u32 *cs_mask_arr, enum mr_number mr_num, u32 data, u32 mask);
+int ddr3_tip_write_cs_result(u32 dev_num, u32 offset);
+int ddr3_tip_reset_fifo_ptr(u32 dev_num);
+int ddr3_tip_read_pup_value(u32 dev_num, u32 pup_values[], int reg_addr, u32 mask);
+int ddr3_tip_read_adll_value(u32 dev_num, u32 pup_values[], u32 reg_addr, u32 mask);
+int ddr3_tip_write_adll_value(u32 dev_num, u32 pup_values[], u32 reg_addr);
+int ddr3_tip_tune_training_params(u32 dev_num, struct tune_train_params *params);
+
+#endif /* _DDR3_TRAINING_IP_FLOW_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_pbs.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_pbs.h
new file mode 100644
index 0000000..323c67a
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_pbs.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_PBS_H_
+#define _DDR3_TRAINING_IP_PBS_H_
+
+enum {
+	EBA_CONFIG,
+	EEBA_CONFIG,
+	SBA_CONFIG
+};
+
+enum hws_training_load_op {
+	TRAINING_LOAD_OPERATION_UNLOAD,
+	TRAINING_LOAD_OPERATION_LOAD
+};
+
+enum hws_edge {
+	TRAINING_EDGE_1,
+	TRAINING_EDGE_2
+};
+
+enum hws_edge_search {
+	TRAINING_EDGE_MAX,
+	TRAINING_EDGE_MIN
+};
+
+enum pbs_dir {
+	PBS_TX_MODE = 0,
+	PBS_RX_MODE,
+	NUM_OF_PBS_MODES
+};
+
+int ddr3_tip_pbs_rx(u32 dev_num);
+int ddr3_tip_print_all_pbs_result(u32 dev_num);
+int ddr3_tip_pbs_tx(u32 dev_num);
+
+#endif /* _DDR3_TRAINING_IP_PBS_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_prv_if.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_prv_if.h
new file mode 100644
index 0000000..2df592e
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_prv_if.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_IP_PRV_IF_H
+#define _DDR3_TRAINING_IP_PRV_IF_H
+
+#include "ddr3_training_ip.h"
+#include "ddr3_training_ip_flow.h"
+#include "ddr3_training_ip_bist.h"
+
+enum hws_static_config_type {
+	WRITE_LEVELING_STATIC,
+	READ_LEVELING_STATIC
+};
+
+struct ddr3_device_info {
+	u32 device_id;
+	u32 ck_delay;
+};
+
+typedef int (*HWS_TIP_DUNIT_MUX_SELECT_FUNC_PTR)(u8 dev_num, int enable);
+typedef int (*HWS_TIP_DUNIT_REG_READ_FUNC_PTR)(
+	u8 dev_num, enum hws_access_type interface_access, u32 if_id,
+	u32 offset, u32 *data, u32 mask);
+typedef int (*HWS_TIP_DUNIT_REG_WRITE_FUNC_PTR)(
+	u8 dev_num, enum hws_access_type interface_access, u32 if_id,
+	u32 offset, u32 data, u32 mask);
+typedef int (*HWS_TIP_GET_FREQ_CONFIG_INFO)(
+	u8 dev_num, enum mv_ddr_freq freq,
+	struct hws_tip_freq_config_info *freq_config_info);
+typedef int (*HWS_TIP_GET_DEVICE_INFO)(
+	u8 dev_num, struct ddr3_device_info *info_ptr);
+typedef int (*HWS_GET_CS_CONFIG_FUNC_PTR)(
+	u8 dev_num, u32 cs_mask, struct hws_cs_config_info *cs_info);
+typedef int (*HWS_SET_FREQ_DIVIDER_FUNC_PTR)(
+	u8 dev_num, u32 if_id, enum mv_ddr_freq freq);
+typedef int (*HWS_GET_INIT_FREQ)(u8 dev_num, enum mv_ddr_freq *freq);
+typedef int (*HWS_TRAINING_IP_IF_WRITE_FUNC_PTR)(
+	u32 dev_num, enum hws_access_type access_type, u32 dunit_id,
+	u32 reg_addr, u32 data, u32 mask);
+typedef int (*HWS_TRAINING_IP_IF_READ_FUNC_PTR)(
+	u32 dev_num, enum hws_access_type access_type, u32 dunit_id,
+	u32 reg_addr, u32 *data, u32 mask);
+typedef int (*HWS_TRAINING_IP_BUS_WRITE_FUNC_PTR)(
+	u32 dev_num, enum hws_access_type dunit_access_type, u32 if_id,
+	enum hws_access_type phy_access_type, u32 phy_id,
+	enum hws_ddr_phy phy_type, u32 reg_addr, u32 data);
+typedef int (*HWS_TRAINING_IP_BUS_READ_FUNC_PTR)(
+	u32 dev_num, u32 if_id, enum hws_access_type phy_access_type,
+	u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr, u32 *data);
+typedef int (*HWS_TRAINING_IP_ALGO_RUN_FUNC_PTR)(
+	u32 dev_num, enum hws_algo_type algo_type);
+typedef int (*HWS_TRAINING_IP_SET_FREQ_FUNC_PTR)(
+	u32 dev_num, enum hws_access_type access_type, u32 if_id,
+	enum mv_ddr_freq frequency);
+typedef int (*HWS_TRAINING_IP_INIT_CONTROLLER_FUNC_PTR)(
+	u32 dev_num, struct init_cntr_param *init_cntr_prm);
+typedef int (*HWS_TRAINING_IP_PBS_RX_FUNC_PTR)(u32 dev_num);
+typedef int (*HWS_TRAINING_IP_PBS_TX_FUNC_PTR)(u32 dev_num);
+typedef int (*HWS_TRAINING_IP_SELECT_CONTROLLER_FUNC_PTR)(
+	u32 dev_num, int enable);
+typedef int (*HWS_TRAINING_IP_TOPOLOGY_MAP_LOAD_FUNC_PTR)(
+	u32 dev_num, struct mv_ddr_topology_map *tm);
+typedef int (*HWS_TRAINING_IP_STATIC_CONFIG_FUNC_PTR)(
+	u32 dev_num, enum mv_ddr_freq frequency,
+	enum hws_static_config_type static_config_type, u32 if_id);
+typedef int (*HWS_TRAINING_IP_EXTERNAL_READ_PTR)(
+	u32 dev_num, u32 if_id, u32 ddr_addr, u32 num_bursts, u32 *data);
+typedef int (*HWS_TRAINING_IP_EXTERNAL_WRITE_PTR)(
+	u32 dev_num, u32 if_id, u32 ddr_addr, u32 num_bursts, u32 *data);
+typedef int (*HWS_TRAINING_IP_BIST_ACTIVATE)(
+	u32 dev_num, enum hws_pattern pattern, enum hws_access_type access_type,
+	u32 if_num, enum hws_dir direction,
+	enum hws_stress_jump addr_stress_jump,
+	enum hws_pattern_duration duration,
+	enum hws_bist_operation oper_type, u32 offset, u32 cs_num,
+	u32 pattern_addr_length);
+typedef int (*HWS_TRAINING_IP_BIST_READ_RESULT)(
+	u32 dev_num, u32 if_id, struct bist_result *pst_bist_result);
+typedef int (*HWS_TRAINING_IP_LOAD_TOPOLOGY)(u32 dev_num, u32 config_num);
+typedef int (*HWS_TRAINING_IP_READ_LEVELING)(u32 dev_num, u32 config_num);
+typedef int (*HWS_TRAINING_IP_WRITE_LEVELING)(u32 dev_num, u32 config_num);
+typedef u32 (*HWS_TRAINING_IP_GET_TEMP)(u8 dev_num);
+typedef u8 (*HWS_TRAINING_IP_GET_RATIO)(u32 freq);
+
+struct hws_tip_config_func_db {
+	HWS_TIP_DUNIT_MUX_SELECT_FUNC_PTR tip_dunit_mux_select_func;
+	void (*mv_ddr_dunit_read)(u32 addr, u32 mask, u32 *data);
+	void (*mv_ddr_dunit_write)(u32 addr, u32 mask, u32 data);
+	HWS_TIP_GET_FREQ_CONFIG_INFO tip_get_freq_config_info_func;
+	HWS_TIP_GET_DEVICE_INFO tip_get_device_info_func;
+	HWS_SET_FREQ_DIVIDER_FUNC_PTR tip_set_freq_divider_func;
+	HWS_GET_CS_CONFIG_FUNC_PTR tip_get_cs_config_info;
+	HWS_TRAINING_IP_GET_TEMP tip_get_temperature;
+	HWS_TRAINING_IP_GET_RATIO tip_get_clock_ratio;
+	HWS_TRAINING_IP_EXTERNAL_READ_PTR tip_external_read;
+	HWS_TRAINING_IP_EXTERNAL_WRITE_PTR tip_external_write;
+	int (*mv_ddr_phy_read)(enum hws_access_type phy_access,
+			       u32 phy, enum hws_ddr_phy phy_type,
+			       u32 reg_addr, u32 *data);
+	int (*mv_ddr_phy_write)(enum hws_access_type phy_access,
+				u32 phy, enum hws_ddr_phy phy_type,
+				u32 reg_addr, u32 data,
+				enum hws_operation op_type);
+};
+
+int ddr3_tip_init_config_func(u32 dev_num,
+			      struct hws_tip_config_func_db *config_func);
+int ddr3_tip_register_xsb_info(u32 dev_num,
+			       struct hws_xsb_info *xsb_info_table);
+enum hws_result *ddr3_tip_get_result_ptr(u32 stage);
+int ddr3_set_freq_config_info(struct hws_tip_freq_config_info *table);
+int print_device_info(u8 dev_num);
+
+#endif /* _DDR3_TRAINING_IP_PRV_IF_H */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_leveling.c b/drivers/ddr/marvell/a38x/ddr3_training_leveling.c
new file mode 100644
index 0000000..7f7df67
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_leveling.c
@@ -0,0 +1,1965 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_training_db.h"
+#include "ddr_training_ip_db.h"
+#include "mv_ddr_regs.h"
+
+#define WL_ITERATION_NUM	10
+
+static u32 pup_mask_table[] = {
+	0x000000ff,
+	0x0000ff00,
+	0x00ff0000,
+	0xff000000
+};
+
+static struct write_supp_result wr_supp_res[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+
+static int ddr3_tip_dynamic_write_leveling_seq(u32 dev_num);
+static int ddr3_tip_dynamic_read_leveling_seq(u32 dev_num);
+static int ddr3_tip_dynamic_per_bit_read_leveling_seq(u32 dev_num);
+static int ddr3_tip_wl_supp_align_phase_shift(u32 dev_num, u32 if_id,
+					      u32 bus_id);
+static int ddr3_tip_xsb_compare_test(u32 dev_num, u32 if_id, u32 bus_id,
+				     u32 edge_offset);
+
+enum {
+	PASS,
+	FAIL
+};
+/*****************************************************************************
+Dynamic read leveling
+******************************************************************************/
+int ddr3_tip_dynamic_read_leveling(u32 dev_num, u32 freq)
+{
+	u32 data, mask;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	u32 bus_num, if_id, cl_val;
+	enum mv_ddr_speed_bin speed_bin_index;
+	/* save current CS value */
+	u32 cs_enable_reg_val[MAX_INTERFACE_NUM] = { 0 };
+	int is_any_pup_fail = 0;
+	u32 data_read[MAX_INTERFACE_NUM + 1] = { 0 };
+	u8 rl_values[MAX_CS_NUM][MAX_BUS_NUM][MAX_INTERFACE_NUM];
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (effective_cs = 0; effective_cs < MAX_CS_NUM; effective_cs++)
+		for (bus_num = 0; bus_num < MAX_BUS_NUM; bus_num++)
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++)
+				rl_values[effective_cs][bus_num][if_id] = 0;
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			training_result[training_stage][if_id] = TEST_SUCCESS;
+
+			/* save current cs enable reg val */
+			CHECK_STATUS(ddr3_tip_if_read
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      DUAL_DUNIT_CFG_REG, cs_enable_reg_val,
+				      MASK_ALL_BITS));
+			/* enable single cs */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      DUAL_DUNIT_CFG_REG, (1 << 3), (1 << 3)));
+		}
+
+		ddr3_tip_reset_fifo_ptr(dev_num);
+
+		/*
+		 *     Phase 1: Load pattern (using ODPG)
+		 *
+		 * enter Read Leveling mode
+		 * only 27 bits are masked
+		 * assuming non multi-CS configuration
+		 * write to CS = 0 for the non multi CS configuration, note
+		 * that the results shall be read back to the required CS !!!
+		 */
+
+		/* BUS count is 0 shifted 26 */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_DATA_CTRL_REG, 0x3, 0x3));
+		CHECK_STATUS(ddr3_tip_configure_odpg
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0,
+			      pattern_table[PATTERN_RL].num_of_phases_tx, 0,
+			      pattern_table[PATTERN_RL].num_of_phases_rx, 0, 0,
+			      effective_cs, STRESS_NONE, DURATION_SINGLE));
+
+		/* load pattern to ODPG */
+		ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST,
+					      PARAM_NOT_CARE, PATTERN_RL,
+					      pattern_table[PATTERN_RL].
+					      start_addr);
+
+		/*
+		 *     Phase 2: ODPG to Read Leveling mode
+		 */
+
+		/* General Training Opcode register */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_WR_RD_MODE_ENA_REG, 0,
+			      MASK_ALL_BITS));
+
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      GENERAL_TRAINING_OPCODE_REG,
+			      (0x301b01 | effective_cs << 2), 0x3c3fef));
+
+		/* Object1 opcode register 0 & 1 */
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			speed_bin_index =
+				tm->interface_params[if_id].speed_bin_index;
+			cl_val = mv_ddr_cl_val_get(speed_bin_index, freq);
+			data = (cl_val << 17) | (0x3 << 25);
+			mask = (0xff << 9) | (0x1f << 17) | (0x3 << 25);
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      OPCODE_REG0_REG(1), data, mask));
+		}
+
+		/* Set iteration count to max value */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      OPCODE_REG1_REG(1), 0xd00, 0xd00));
+
+		/*
+		 *     Phase 2: Mask config
+		 */
+
+		ddr3_tip_dynamic_read_leveling_seq(dev_num);
+
+		/*
+		 *     Phase 3: Read Leveling execution
+		 */
+
+		/* temporary jira dunit=14751 */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_DBG_1_REG, 0, (u32)(1 << 31)));
+		/* configure phy reset value */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_DBG_3_REG, (0x7f << 24),
+			      (u32)(0xff << 24)));
+		/* data pup rd reset enable  */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      SDRAM_CFG_REG, 0, (1 << 30)));
+		/* data pup rd reset disable */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      SDRAM_CFG_REG, (1 << 30), (1 << 30)));
+		/* training SW override & training RL mode */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x1, 0x9));
+		/* training enable */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_REG, (1 << 24) | (1 << 20),
+			      (1 << 24) | (1 << 20)));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_REG, (u32)(1 << 31), (u32)(1 << 31)));
+
+		/* trigger training */
+		mv_ddr_training_enable();
+
+		/* check for training done */
+		if (mv_ddr_is_training_done(MAX_POLLING_ITERATIONS, &data) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR, ("training done failed\n"));
+			return MV_FAIL;
+		}
+		/* check for training pass */
+		if (data != PASS)
+			DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("training result failed\n"));
+
+		/* disable odpg; switch back to functional mode */
+		mv_ddr_odpg_disable();
+
+		if (mv_ddr_is_odpg_done(MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR, ("odpg disable failed\n"));
+			return MV_FAIL;
+		}
+
+		ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+				  ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS);
+
+		/* double loop on bus, pup */
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			/* check training done */
+			is_any_pup_fail = 0;
+			for (bus_num = 0;
+			     bus_num < octets_per_if_num;
+			     bus_num++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_num);
+				if (ddr3_tip_if_polling
+				    (dev_num, ACCESS_TYPE_UNICAST,
+				     if_id, (1 << 25), (1 << 25),
+				     mask_results_pup_reg_map[bus_num],
+				     MAX_POLLING_ITERATIONS) != MV_OK) {
+					DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+						       ("\n_r_l: DDR3 poll failed(2) for IF %d CS %d bus %d",
+							if_id, effective_cs, bus_num));
+					is_any_pup_fail = 1;
+				} else {
+					/* read result per pup */
+					CHECK_STATUS(ddr3_tip_if_read
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      mask_results_pup_reg_map
+						      [bus_num], data_read,
+						      0xff));
+					rl_values[effective_cs][bus_num]
+						[if_id] = (u8)data_read[if_id];
+				}
+			}
+
+			if (is_any_pup_fail == 1) {
+				training_result[training_stage][if_id] =
+					TEST_FAILED;
+				if (debug_mode == 0)
+					return MV_FAIL;
+			}
+		}
+
+		DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("RL exit read leveling\n"));
+
+		/*
+		 *     Phase 3: Exit Read Leveling
+		 */
+
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, (1 << 3), (1 << 3)));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_1_REG, (1 << 16), (1 << 16)));
+		/* set ODPG to functional */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_DATA_CTRL_REG, 0x0, MASK_ALL_BITS));
+
+		/*
+		 * Copy the result from the effective CS search to the
+		 * real Functional CS
+		 */
+		/*ddr3_tip_write_cs_result(dev_num, RL_PHY_REG(0); */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_DATA_CTRL_REG, 0x0, MASK_ALL_BITS));
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		/* double loop on bus, pup */
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			for (bus_num = 0;
+			     bus_num < octets_per_if_num;
+			     bus_num++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_num);
+				/* read result per pup from arry */
+				data = rl_values[effective_cs][bus_num][if_id];
+				data = (data & 0x1f) |
+					(((data & 0xe0) >> 5) << 6);
+				ddr3_tip_bus_write(dev_num,
+						   ACCESS_TYPE_UNICAST,
+						   if_id,
+						   ACCESS_TYPE_UNICAST,
+						   bus_num, DDR_PHY_DATA,
+						   RL_PHY_REG(effective_cs),
+						   data);
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* restore cs enable value */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val[if_id],
+			      MASK_ALL_BITS));
+		if (odt_config != 0) {
+			CHECK_STATUS(ddr3_tip_write_additional_odt_setting
+				     (dev_num, if_id));
+		}
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (training_result[training_stage][if_id] == TEST_FAILED)
+			return MV_FAIL;
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Legacy Dynamic write leveling
+ */
+int ddr3_tip_legacy_dynamic_write_leveling(u32 dev_num)
+{
+	u32 c_cs, if_id, cs_mask = 0;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/*
+	 * In TRAINIUNG reg (0x15b0) write 0x80000008 | cs_mask:
+	 * Trn_start
+	 * cs_mask = 0x1 <<20 Trn_CS0 - CS0 is included in the DDR3 training
+	 * cs_mask = 0x1 <<21 Trn_CS1 - CS1 is included in the DDR3 training
+	 * cs_mask = 0x1 <<22 Trn_CS2 - CS2 is included in the DDR3 training
+	 * cs_mask = 0x1 <<23 Trn_CS3 - CS3 is included in the DDR3 training
+	 * Trn_auto_seq =  write leveling
+	 */
+	for (c_cs = 0; c_cs < max_cs; c_cs++)
+		cs_mask = cs_mask | 1 << (20 + c_cs);
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, 0,
+			      TRAINING_REG, (0x80000008 | cs_mask),
+			      0xffffffff));
+		mdelay(20);
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0,
+		     (u32)0x80000000, TRAINING_REG,
+		     MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+				       ("polling failed for Old WL result\n"));
+			return MV_FAIL;
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Legacy Dynamic read leveling
+ */
+int ddr3_tip_legacy_dynamic_read_leveling(u32 dev_num)
+{
+	u32 c_cs, if_id, cs_mask = 0;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/*
+	 * In TRAINIUNG reg (0x15b0) write 0x80000040 | cs_mask:
+	 * Trn_start
+	 * cs_mask = 0x1 <<20 Trn_CS0 - CS0 is included in the DDR3 training
+	 * cs_mask = 0x1 <<21 Trn_CS1 - CS1 is included in the DDR3 training
+	 * cs_mask = 0x1 <<22 Trn_CS2 - CS2 is included in the DDR3 training
+	 * cs_mask = 0x1 <<23 Trn_CS3 - CS3 is included in the DDR3 training
+	 * Trn_auto_seq =  Read Leveling using training pattern
+	 */
+	for (c_cs = 0; c_cs < max_cs; c_cs++)
+		cs_mask = cs_mask | 1 << (20 + c_cs);
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, 0, TRAINING_REG,
+		      (0x80000040 | cs_mask), 0xffffffff));
+	mdelay(100);
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0,
+		     (u32)0x80000000, TRAINING_REG,
+		     MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+				       ("polling failed for Old RL result\n"));
+			return MV_FAIL;
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Dynamic per bit read leveling
+ */
+int ddr3_tip_dynamic_per_bit_read_leveling(u32 dev_num, u32 freq)
+{
+	u32 data, mask;
+	u32 bus_num, if_id, cl_val, bit_num;
+	u32 curr_numb, curr_min_delay;
+	int adll_array[3] = { 0, -0xa, 0x14 };
+	u32 phyreg3_arr[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+	enum mv_ddr_speed_bin speed_bin_index;
+	int is_any_pup_fail = 0;
+	int break_loop = 0;
+	u32 cs_enable_reg_val[MAX_INTERFACE_NUM]; /* save current CS value */
+	u32 data_read[MAX_INTERFACE_NUM];
+	int per_bit_rl_pup_status[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+	u32 data2_write[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_num = 0;
+		     bus_num <= octets_per_if_num; bus_num++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_num);
+			per_bit_rl_pup_status[if_id][bus_num] = 0;
+			data2_write[if_id][bus_num] = 0;
+			/* read current value of phy register 0x3 */
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id, ACCESS_TYPE_UNICAST,
+				      bus_num, DDR_PHY_DATA,
+				      CRX_PHY_REG(0),
+				      &phyreg3_arr[if_id][bus_num]));
+		}
+	}
+
+	/* NEW RL machine */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		training_result[training_stage][if_id] = TEST_SUCCESS;
+
+		/* save current cs enable reg val */
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, &cs_enable_reg_val[if_id],
+			      MASK_ALL_BITS));
+		/* enable single cs */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, (1 << 3), (1 << 3)));
+	}
+
+	ddr3_tip_reset_fifo_ptr(dev_num);
+	for (curr_numb = 0; curr_numb < 3; curr_numb++) {
+		/*
+		 *     Phase 1: Load pattern (using ODPG)
+		 *
+		 * enter Read Leveling mode
+		 * only 27 bits are masked
+		 * assuming non multi-CS configuration
+		 * write to CS = 0 for the non multi CS configuration, note that
+		 * the results shall be read back to the required CS !!!
+		 */
+
+		/* BUS count is 0 shifted 26 */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_DATA_CTRL_REG, 0x3, 0x3));
+		CHECK_STATUS(ddr3_tip_configure_odpg
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0,
+			      pattern_table[PATTERN_TEST].num_of_phases_tx, 0,
+			      pattern_table[PATTERN_TEST].num_of_phases_rx, 0,
+			      0, 0, STRESS_NONE, DURATION_SINGLE));
+
+		/* load pattern to ODPG */
+		ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST,
+					      PARAM_NOT_CARE, PATTERN_TEST,
+					      pattern_table[PATTERN_TEST].
+					      start_addr);
+
+		/*
+		 *     Phase 2: ODPG to Read Leveling mode
+		 */
+
+		/* General Training Opcode register */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      ODPG_WR_RD_MODE_ENA_REG, 0,
+			      MASK_ALL_BITS));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      GENERAL_TRAINING_OPCODE_REG, 0x301b01, 0x3c3fef));
+
+		/* Object1 opcode register 0 & 1 */
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			speed_bin_index =
+				tm->interface_params[if_id].speed_bin_index;
+			cl_val = mv_ddr_cl_val_get(speed_bin_index, freq);
+			data = (cl_val << 17) | (0x3 << 25);
+			mask = (0xff << 9) | (0x1f << 17) | (0x3 << 25);
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      OPCODE_REG0_REG(1), data, mask));
+		}
+
+		/* Set iteration count to max value */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      OPCODE_REG1_REG(1), 0xd00, 0xd00));
+
+		/*
+		 *     Phase 2: Mask config
+		 */
+
+		ddr3_tip_dynamic_per_bit_read_leveling_seq(dev_num);
+
+		/*
+		 *     Phase 3: Read Leveling execution
+		 */
+
+		/* temporary jira dunit=14751 */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_DBG_1_REG, 0, (u32)(1 << 31)));
+		/* configure phy reset value */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_DBG_3_REG, (0x7f << 24),
+			      (u32)(0xff << 24)));
+		/* data pup rd reset enable  */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      SDRAM_CFG_REG, 0, (1 << 30)));
+		/* data pup rd reset disable */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      SDRAM_CFG_REG, (1 << 30), (1 << 30)));
+		/* training SW override & training RL mode */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x1, 0x9));
+		/* training enable */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_REG, (1 << 24) | (1 << 20),
+			      (1 << 24) | (1 << 20)));
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_REG, (u32)(1 << 31), (u32)(1 << 31)));
+
+		/* trigger training */
+		mv_ddr_training_enable();
+
+		/* check for training done */
+		if (mv_ddr_is_training_done(MAX_POLLING_ITERATIONS, &data) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR, ("training done failed\n"));
+			return MV_FAIL;
+		}
+		/* check for training pass */
+		if (data != PASS)
+			DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("training result failed\n"));
+
+		/* disable odpg; switch back to functional mode */
+		mv_ddr_odpg_disable();
+
+		if (mv_ddr_is_odpg_done(MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR, ("odpg disable failed\n"));
+			return MV_FAIL;
+		}
+
+		ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+				  ODPG_DATA_CTRL_REG, 0, MASK_ALL_BITS);
+
+		/* double loop on bus, pup */
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			/* check training done */
+			for (bus_num = 0;
+			     bus_num < octets_per_if_num;
+			     bus_num++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_num);
+
+				if (per_bit_rl_pup_status[if_id][bus_num]
+				    == 0) {
+					curr_min_delay = 0;
+					for (bit_num = 0; bit_num < 8;
+					     bit_num++) {
+						if (ddr3_tip_if_polling
+						    (dev_num,
+						     ACCESS_TYPE_UNICAST,
+						     if_id, (1 << 25),
+						     (1 << 25),
+						     mask_results_dq_reg_map
+						     [bus_num * 8 + bit_num],
+						     MAX_POLLING_ITERATIONS) !=
+						    MV_OK) {
+							DEBUG_LEVELING
+								(DEBUG_LEVEL_ERROR,
+								 ("\n_r_l: DDR3 poll failed(2) for bus %d bit %d\n",
+								  bus_num,
+								  bit_num));
+						} else {
+							/* read result per pup */
+							CHECK_STATUS
+								(ddr3_tip_if_read
+								 (dev_num,
+								  ACCESS_TYPE_UNICAST,
+								  if_id,
+								  mask_results_dq_reg_map
+								  [bus_num * 8 +
+								   bit_num],
+								  data_read,
+								  MASK_ALL_BITS));
+							data =
+								(data_read
+								 [if_id] &
+								 0x1f) |
+								((data_read
+								  [if_id] &
+								  0xe0) << 1);
+							if (curr_min_delay == 0)
+								curr_min_delay =
+									data;
+							else if (data <
+								 curr_min_delay)
+								curr_min_delay =
+									data;
+							if (data > data2_write[if_id][bus_num])
+								data2_write
+									[if_id]
+									[bus_num] =
+									data;
+						}
+					}
+
+					if (data2_write[if_id][bus_num] <=
+					    (curr_min_delay +
+					     MAX_DQ_READ_LEVELING_DELAY)) {
+						per_bit_rl_pup_status[if_id]
+							[bus_num] = 1;
+					}
+				}
+			}
+		}
+
+		/* check if there is need to search new phyreg3 value */
+		if (curr_numb < 2) {
+			/* if there is DLL that is not checked yet */
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				for (bus_num = 0;
+				     bus_num < octets_per_if_num;
+				     bus_num++) {
+					VALIDATE_BUS_ACTIVE(tm->bus_act_mask,
+							bus_num);
+					if (per_bit_rl_pup_status[if_id]
+					    [bus_num] != 1) {
+						/* go to next ADLL value */
+						CHECK_STATUS
+							(ddr3_tip_bus_write
+							 (dev_num,
+							  ACCESS_TYPE_UNICAST,
+							  if_id,
+							  ACCESS_TYPE_UNICAST,
+							  bus_num, DDR_PHY_DATA,
+							  CRX_PHY_REG(0),
+							  (phyreg3_arr[if_id]
+							   [bus_num] +
+							   adll_array[curr_numb])));
+						break_loop = 1;
+						break;
+					}
+				}
+				if (break_loop)
+					break;
+			}
+		}		/* if (curr_numb < 2) */
+		if (!break_loop)
+			break;
+	}		/* for ( curr_numb = 0; curr_numb <3; curr_numb++) */
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_num = 0; bus_num < octets_per_if_num;
+		     bus_num++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_num);
+			if (per_bit_rl_pup_status[if_id][bus_num] == 1)
+				ddr3_tip_bus_write(dev_num,
+						   ACCESS_TYPE_UNICAST,
+						   if_id,
+						   ACCESS_TYPE_UNICAST,
+						   bus_num, DDR_PHY_DATA,
+						   RL_PHY_REG(effective_cs),
+						   data2_write[if_id]
+						   [bus_num]);
+			else
+				is_any_pup_fail = 1;
+		}
+
+		/* TBD flow does not support multi CS */
+		/*
+		 * cs_bitmask = tm->interface_params[if_id].
+		 * as_bus_params[bus_num].cs_bitmask;
+		 */
+		/* divide by 4 is used for retrieving the CS number */
+		/*
+		 * TBD BC2 - what is the PHY address for other
+		 * CS ddr3_tip_write_cs_result() ???
+		 */
+		/*
+		 * find what should be written to PHY
+		 * - max delay that is less than threshold
+		 */
+		if (is_any_pup_fail == 1) {
+			training_result[training_stage][if_id] = TEST_FAILED;
+			if (debug_mode == 0)
+				return MV_FAIL;
+		}
+	}
+	DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("RL exit read leveling\n"));
+
+	/*
+	 *     Phase 3: Exit Read Leveling
+	 */
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      TRAINING_SW_2_REG, (1 << 3), (1 << 3)));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      TRAINING_SW_1_REG, (1 << 16), (1 << 16)));
+	/* set ODPG to functional */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_CTRL_REG, 0x0, MASK_ALL_BITS));
+	/*
+	 * Copy the result from the effective CS search to the real
+	 * Functional CS
+	 */
+	ddr3_tip_write_cs_result(dev_num, RL_PHY_REG(0));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_DATA_CTRL_REG, 0x0, MASK_ALL_BITS));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* restore cs enable value */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val[if_id],
+			      MASK_ALL_BITS));
+		if (odt_config != 0) {
+			CHECK_STATUS(ddr3_tip_write_additional_odt_setting
+				     (dev_num, if_id));
+		}
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (training_result[training_stage][if_id] == TEST_FAILED)
+			return MV_FAIL;
+	}
+
+	return MV_OK;
+}
+
+int ddr3_tip_calc_cs_mask(u32 dev_num, u32 if_id, u32 effective_cs,
+			  u32 *cs_mask)
+{
+	u32 all_bus_cs = 0, same_bus_cs;
+	u32 bus_cnt;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	*cs_mask = same_bus_cs = CS_BIT_MASK;
+
+	/*
+	 * In some of the devices (such as BC2), the CS is per pup and there
+	 * for mixed mode is valid on like other devices where CS configuration
+	 * is per interface.
+	 * In order to know that, we do 'Or' and 'And' operation between all
+	 * CS (of the pups).
+	 * If they are they are not the same then it's mixed mode so all CS
+	 * should be configured (when configuring the MRS)
+	 */
+	for (bus_cnt = 0; bus_cnt < octets_per_if_num; bus_cnt++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+
+		all_bus_cs |= tm->interface_params[if_id].
+			as_bus_params[bus_cnt].cs_bitmask;
+		same_bus_cs &= tm->interface_params[if_id].
+			as_bus_params[bus_cnt].cs_bitmask;
+
+		/* cs enable is active low */
+		*cs_mask &= ~tm->interface_params[if_id].
+			as_bus_params[bus_cnt].cs_bitmask;
+	}
+
+	if (all_bus_cs == same_bus_cs)
+		*cs_mask = (*cs_mask | (~(1 << effective_cs))) & CS_BIT_MASK;
+
+	return MV_OK;
+}
+
+/*
+ * Dynamic write leveling
+ */
+int ddr3_tip_dynamic_write_leveling(u32 dev_num, int phase_remove)
+{
+	u32 reg_data = 0, temp = 0, iter, if_id, bus_cnt;
+	u32 cs_enable_reg_val[MAX_INTERFACE_NUM] = { 0 };
+	u32 cs_mask[MAX_INTERFACE_NUM];
+	u32 read_data_sample_delay_vals[MAX_INTERFACE_NUM] = { 0 };
+	u32 read_data_ready_delay_vals[MAX_INTERFACE_NUM] = { 0 };
+	/* 0 for failure */
+	u32 res_values[MAX_INTERFACE_NUM * MAX_BUS_NUM] = { 0 };
+	u32 test_res = 0;	/* 0 - success for all pup */
+	u32 data_read[MAX_INTERFACE_NUM];
+	u8 wl_values[MAX_CS_NUM][MAX_BUS_NUM][MAX_INTERFACE_NUM];
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u32 cs_mask0[MAX_INTERFACE_NUM] = { 0 };
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		training_result[training_stage][if_id] = TEST_SUCCESS;
+
+		/* save Read Data Sample Delay */
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      RD_DATA_SMPL_DLYS_REG,
+			      read_data_sample_delay_vals, MASK_ALL_BITS));
+		/* save Read Data Ready Delay */
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      RD_DATA_RDY_DLYS_REG, read_data_ready_delay_vals,
+			      MASK_ALL_BITS));
+		/* save current cs reg val */
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val, MASK_ALL_BITS));
+	}
+
+	if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) < MV_TIP_REV_3) {
+		/* Enable multi-CS */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			     DUAL_DUNIT_CFG_REG, 0, (1 << 3)));
+	}
+
+	/*
+	 *     Phase 1: DRAM 2 Write Leveling mode
+	 */
+
+	/*Assert 10 refresh commands to DRAM to all CS */
+	for (iter = 0; iter < WL_ITERATION_NUM; iter++) {
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_UNICAST,
+				      if_id, SDRAM_OP_REG,
+				      (u32)((~(0xf) << 8) | 0x2), 0xf1f));
+		}
+	}
+	/* check controller back to normal */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (ddr3_tip_if_polling
+		    (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x1f,
+		     SDRAM_OP_REG, MAX_POLLING_ITERATIONS) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+				       ("WL: DDR3 poll failed(3)"));
+		}
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		/*enable write leveling to all cs  - Q off , WL n */
+		/* calculate interface cs mask */
+		CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask0, MR_CMD1,
+						    0x1000, 0x1080));
+
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			/* cs enable is active low */
+			ddr3_tip_calc_cs_mask(dev_num, if_id, effective_cs,
+					      &cs_mask[if_id]);
+		}
+
+		if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) >= MV_TIP_REV_3) {
+			/* Enable Output buffer to relevant CS - Q on , WL on */
+			CHECK_STATUS(ddr3_tip_write_mrs_cmd
+				     (dev_num, cs_mask, MR_CMD1, 0x80, 0x1080));
+
+			/*enable odt for relevant CS */
+			CHECK_STATUS(ddr3_tip_if_write
+				     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+				      0x1498, (0x3 << (effective_cs * 2)), 0xf));
+		} else {
+			/* FIXME: should be the same as _CPU case */
+			CHECK_STATUS(ddr3_tip_write_mrs_cmd
+				     (dev_num, cs_mask, MR_CMD1, 0xc0, 0x12c4));
+		}
+
+		/*
+		 *     Phase 2: Set training IP to write leveling mode
+		 */
+
+		CHECK_STATUS(ddr3_tip_dynamic_write_leveling_seq(dev_num));
+
+		/* phase 3: trigger training */
+		mv_ddr_training_enable();
+
+		/* check for training done */
+		if (mv_ddr_is_training_done(MAX_POLLING_ITERATIONS, data_read) != MV_OK) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR, ("training done failed\n"));
+		} else { /* check for training pass */
+			reg_data = data_read[0];
+#if defined(CONFIG_ARMADA_38X) /* JIRA #1498 for 16 bit with ECC */
+			if (tm->bus_act_mask == 0xb) /* set to data to 0 to skip the check */
+				reg_data = 0;
+#endif
+			if (reg_data != PASS)
+				DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("training result failed\n"));
+
+			/* check for training completion per bus */
+			for (bus_cnt = 0; bus_cnt < octets_per_if_num; bus_cnt++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+				/* training status */
+				ddr3_tip_if_read(0, ACCESS_TYPE_UNICAST, 0,
+					      mask_results_pup_reg_map[bus_cnt],
+					      data_read, MASK_ALL_BITS);
+				reg_data = data_read[0];
+				DEBUG_LEVELING(DEBUG_LEVEL_TRACE, ("WL: IF %d BUS %d reg 0x%x\n",
+								   0, bus_cnt, reg_data));
+				if ((reg_data & (1 << 25)) == 0)
+					res_values[bus_cnt] = 1;
+				ddr3_tip_if_read(0, ACCESS_TYPE_UNICAST, 0,
+					      mask_results_pup_reg_map[bus_cnt],
+					      data_read, 0xff);
+				/*
+				 * Save the read value that should be
+				 * write to PHY register
+				 */
+				wl_values[effective_cs][bus_cnt][0] = (u8)data_read[0];
+			}
+		}
+
+		/*
+		 *     Phase 3.5: Validate result
+		 */
+		for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			for (bus_cnt = 0; bus_cnt < octets_per_if_num; bus_cnt++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+				/*
+				 * Read result control register according to subphy
+				 * "16" below is for a half-phase
+				 */
+				reg_data = wl_values[effective_cs][bus_cnt][if_id] + 16;
+				/*
+				 * Write to WL register: ADLL [4:0], Phase [8:6],
+				 * Centralization ADLL [15:10] + 0x10
+				 */
+				reg_data = (reg_data & 0x1f) |
+					   (((reg_data & 0xe0) >> 5) << 6) |
+					   (((reg_data & 0x1f) + phy_reg1_val) << 10);
+				/* Search with WL CS0 subphy reg */
+				ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
+						   ACCESS_TYPE_UNICAST, bus_cnt,
+						   DDR_PHY_DATA, WL_PHY_REG(0), reg_data);
+				/*
+				 * Check for change in data read from DRAM.
+				 * If changed, fix the result
+				 */
+				CHECK_STATUS(ddr3_tip_if_read
+					     (dev_num,
+					      ACCESS_TYPE_UNICAST,
+					      if_id,
+					      TRAINING_WL_REG,
+					      data_read, MASK_ALL_BITS));
+				if (((data_read[if_id] & (1 << (bus_cnt + 20))) >>
+				     (bus_cnt + 20)) == 0) {
+					DEBUG_LEVELING(
+						DEBUG_LEVEL_ERROR,
+						("WLValues was changed from 0x%X",
+						 wl_values[effective_cs]
+						 [bus_cnt][if_id]));
+					wl_values[effective_cs]
+					[bus_cnt][if_id] += 32;
+					DEBUG_LEVELING(
+						DEBUG_LEVEL_ERROR,
+						("to 0x%X",
+						 wl_values[effective_cs]
+						 [bus_cnt][if_id]));
+				}
+			}
+		}
+
+		/*
+		 *     Phase 4: Exit write leveling mode
+		 */
+
+		/* disable DQs toggling */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      WL_DQS_PATTERN_REG, 0x0, 0x1));
+
+		/* Update MRS 1 (WL off) */
+		if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) >= MV_TIP_REV_3) {
+			CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask0, MR_CMD1,
+							    0x1000, 0x1080));
+		} else {
+			/* FIXME: should be same as _CPU case */
+			CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask0, MR_CMD1,
+							    0x1000, 0x12c4));
+		}
+
+		/* Update MRS 1 (return to functional mode - Q on , WL off) */
+		CHECK_STATUS(ddr3_tip_write_mrs_cmd
+			     (dev_num, cs_mask0, MR_CMD1, 0x0, 0x1080));
+
+		/* set phy to normal mode */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x5, 0x7));
+
+		/* exit sw override mode  */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      TRAINING_SW_2_REG, 0x4, 0x7));
+	}
+
+	/*
+	 *     Phase 5: Load WL values to each PHY
+	 */
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			test_res = 0;
+			for (bus_cnt = 0;
+			     bus_cnt < octets_per_if_num;
+			     bus_cnt++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_cnt);
+				/* check if result == pass */
+				if (res_values
+				    [(if_id *
+				      octets_per_if_num) +
+				     bus_cnt] == 0) {
+					/*
+					 * read result control register
+					 * according to pup
+					 */
+					reg_data =
+						wl_values[effective_cs][bus_cnt]
+						[if_id];
+					/*
+					 * Write into write leveling register
+					 * ([4:0] ADLL, [8:6] Phase, [15:10]
+					 * (centralization) ADLL + 0x10)
+					 */
+					reg_data =
+						(reg_data & 0x1f) |
+						(((reg_data & 0xe0) >> 5) << 6) |
+						(((reg_data & 0x1f) +
+						  phy_reg1_val) << 10);
+					/*
+					 * in case phase remove should be executed
+					 * need to remove more than one phase.
+					 * this will take place only in low frequency,
+					 * where there could be more than one phase between sub-phys
+					 */
+					if (phase_remove == 1) {
+						temp = (reg_data >> WR_LVL_PH_SEL_OFFS) & WR_LVL_PH_SEL_PHASE1;
+						reg_data &= ~(WR_LVL_PH_SEL_MASK << WR_LVL_PH_SEL_OFFS);
+						reg_data |= (temp << WR_LVL_PH_SEL_OFFS);
+					}
+
+					ddr3_tip_bus_write(
+						dev_num,
+						ACCESS_TYPE_UNICAST,
+						if_id,
+						ACCESS_TYPE_UNICAST,
+						bus_cnt,
+						DDR_PHY_DATA,
+						WL_PHY_REG(effective_cs),
+						reg_data);
+				} else {
+					test_res = 1;
+					/*
+					 * read result control register
+					 * according to pup
+					 */
+					CHECK_STATUS(ddr3_tip_if_read
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      mask_results_pup_reg_map
+						      [bus_cnt], data_read,
+						      0xff));
+					reg_data = data_read[if_id];
+					DEBUG_LEVELING(
+						DEBUG_LEVEL_ERROR,
+						("WL: IF %d BUS %d failed, reg 0x%x\n",
+						 if_id, bus_cnt, reg_data));
+				}
+			}
+
+			if (test_res != 0) {
+				training_result[training_stage][if_id] =
+					TEST_FAILED;
+			}
+		}
+	}
+	/* Set to 0 after each loop to avoid illegal value may be used */
+	effective_cs = 0;
+
+	/*
+	 * Copy the result from the effective CS search to the real
+	 * Functional CS
+	 */
+	/* ddr3_tip_write_cs_result(dev_num, WL_PHY_REG(0); */
+	/* restore saved values */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		/* restore Read Data Sample Delay */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      RD_DATA_SMPL_DLYS_REG,
+			      read_data_sample_delay_vals[if_id],
+			      MASK_ALL_BITS));
+
+		/* restore Read Data Ready Delay */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      RD_DATA_RDY_DLYS_REG,
+			      read_data_ready_delay_vals[if_id],
+			      MASK_ALL_BITS));
+
+		/* enable multi cs */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val[if_id],
+			      MASK_ALL_BITS));
+	}
+
+	if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_TIP_REV) >= MV_TIP_REV_3) {
+		/* Disable modt0 for CS0 training - need to adjust for multi-CS
+		 * in case of ddr4 set 0xf else 0
+		 */
+		if (odt_config != 0) {
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+						       SDRAM_ODT_CTRL_HIGH_REG, 0x0, 0xf));
+		}
+		else {
+			CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+						       SDRAM_ODT_CTRL_HIGH_REG, 0xf, 0xf));
+		}
+
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (training_result[training_stage][if_id] == TEST_FAILED)
+			return MV_FAIL;
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Dynamic write leveling supplementary
+ */
+int ddr3_tip_dynamic_write_leveling_supp(u32 dev_num)
+{
+	int adll_offset;
+	u32 if_id, bus_id, data, data_tmp;
+	int is_if_fail = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		is_if_fail = 0;
+
+		for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			wr_supp_res[if_id][bus_id].is_pup_fail = 1;
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id, ACCESS_TYPE_UNICAST,
+				      bus_id, DDR_PHY_DATA,
+				      CTX_PHY_REG(effective_cs),
+				      &data));
+			DEBUG_LEVELING(
+				DEBUG_LEVEL_TRACE,
+				("WL Supp: adll_offset=0 data delay = %d\n",
+				 data));
+			if (ddr3_tip_wl_supp_align_phase_shift
+			    (dev_num, if_id, bus_id) == MV_OK) {
+				DEBUG_LEVELING(
+					DEBUG_LEVEL_TRACE,
+					("WL Supp: IF %d bus_id %d adll_offset=0 Success !\n",
+					 if_id, bus_id));
+				continue;
+			}
+
+			/* change adll */
+			adll_offset = 5;
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+				      CTX_PHY_REG(effective_cs),
+				      data + adll_offset));
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id, ACCESS_TYPE_UNICAST,
+				      bus_id, DDR_PHY_DATA,
+				      CTX_PHY_REG(effective_cs),
+				      &data_tmp));
+			DEBUG_LEVELING(
+				DEBUG_LEVEL_TRACE,
+				("WL Supp: adll_offset= %d data delay = %d\n",
+				 adll_offset, data_tmp));
+
+			if (ddr3_tip_wl_supp_align_phase_shift
+			    (dev_num, if_id, bus_id) == MV_OK) {
+				DEBUG_LEVELING(
+					DEBUG_LEVEL_TRACE,
+					("WL Supp: IF %d bus_id %d adll_offset= %d Success !\n",
+					 if_id, bus_id, adll_offset));
+				continue;
+			}
+
+			/* change adll */
+			adll_offset = -5;
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+				      CTX_PHY_REG(effective_cs),
+				      data + adll_offset));
+			CHECK_STATUS(ddr3_tip_bus_read
+				     (dev_num, if_id, ACCESS_TYPE_UNICAST,
+				      bus_id, DDR_PHY_DATA,
+				      CTX_PHY_REG(effective_cs),
+				      &data_tmp));
+			DEBUG_LEVELING(
+				DEBUG_LEVEL_TRACE,
+				("WL Supp: adll_offset= %d data delay = %d\n",
+				 adll_offset, data_tmp));
+			if (ddr3_tip_wl_supp_align_phase_shift
+			    (dev_num, if_id, bus_id) == MV_OK) {
+				DEBUG_LEVELING(
+					DEBUG_LEVEL_TRACE,
+					("WL Supp: IF %d bus_id %d adll_offset= %d Success !\n",
+					 if_id, bus_id, adll_offset));
+				continue;
+			} else {
+				DEBUG_LEVELING(
+					DEBUG_LEVEL_ERROR,
+					("WL Supp: IF %d bus_id %d Failed !\n",
+					 if_id, bus_id));
+				is_if_fail = 1;
+			}
+		}
+
+		if (is_if_fail == 1) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+				       ("WL Supp: CS# %d: IF %d failed\n",
+					effective_cs, if_id));
+			training_result[training_stage][if_id] = TEST_FAILED;
+		} else {
+			training_result[training_stage][if_id] = TEST_SUCCESS;
+		}
+	}
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (training_result[training_stage][if_id] == TEST_FAILED)
+			return MV_FAIL;
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Phase Shift
+ */
+static int ddr3_tip_wl_supp_align_phase_shift(u32 dev_num, u32 if_id,
+					      u32 bus_id)
+{
+	u32 original_phase;
+	u32 data, write_data;
+
+	wr_supp_res[if_id][bus_id].stage = PHASE_SHIFT;
+	if (ddr3_tip_xsb_compare_test
+	    (dev_num, if_id, bus_id, 0) == MV_OK)
+		return MV_OK;
+
+	/* Read current phase */
+	CHECK_STATUS(ddr3_tip_bus_read
+		     (dev_num, if_id, ACCESS_TYPE_UNICAST, bus_id,
+		      DDR_PHY_DATA, WL_PHY_REG(effective_cs), &data));
+	original_phase = (data >> 6) & 0x7;
+
+	/* Set phase (0x0[6-8]) -2 */
+	if (original_phase >= 1) {
+		if (original_phase == 1)
+			write_data = data & ~0x1df;
+		else
+			write_data = (data & ~0x1c0) |
+				     ((original_phase - 2) << 6);
+		ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
+				   ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+				   WL_PHY_REG(effective_cs), write_data);
+		if (ddr3_tip_xsb_compare_test
+		    (dev_num, if_id, bus_id, -2) == MV_OK)
+			return MV_OK;
+	}
+
+	/* Set phase (0x0[6-8]) +2 */
+	if (original_phase <= 5) {
+		write_data = (data & ~0x1c0) |
+			     ((original_phase + 2) << 6);
+		ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
+				   ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+				   WL_PHY_REG(effective_cs), write_data);
+		if (ddr3_tip_xsb_compare_test
+		    (dev_num, if_id, bus_id, 2) == MV_OK)
+			return MV_OK;
+	}
+
+	/* Set phase (0x0[6-8]) +4 */
+	if (original_phase <= 3) {
+		write_data = (data & ~0x1c0) |
+			     ((original_phase + 4) << 6);
+		ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
+				   ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+				   WL_PHY_REG(effective_cs), write_data);
+		if (ddr3_tip_xsb_compare_test
+		    (dev_num, if_id, bus_id, 4) == MV_OK)
+			return MV_OK;
+	}
+
+	/* Set phase (0x0[6-8]) +6 */
+	if (original_phase <= 1) {
+		write_data = (data & ~0x1c0) |
+			     ((original_phase + 6) << 6);
+		ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
+				   ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+				   WL_PHY_REG(effective_cs), write_data);
+		if (ddr3_tip_xsb_compare_test
+		    (dev_num, if_id, bus_id, 6) == MV_OK)
+			return MV_OK;
+	}
+
+	/* Write original WL result back */
+	ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
+			   ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA,
+			   WL_PHY_REG(effective_cs), data);
+	wr_supp_res[if_id][bus_id].is_pup_fail = 1;
+
+	return MV_FAIL;
+}
+
+/*
+ * Compare Test
+ */
+static int ddr3_tip_xsb_compare_test(u32 dev_num, u32 if_id, u32 bus_id,
+				     u32 edge_offset)
+{
+	u32 num_of_succ_byte_compare, word_in_pattern;
+	u32 word_offset, i, num_of_word_mult;
+	u32 read_pattern[TEST_PATTERN_LENGTH * 2];
+	struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
+	u32 pattern_test_pattern_table[8];
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* 3 below for INTERFACE_BUS_MASK_16BIT */
+	num_of_word_mult = (tm->bus_act_mask == 3) ? 1 : 2;
+
+	for (i = 0; i < 8; i++) {
+		pattern_test_pattern_table[i] =
+			pattern_table_get_word(dev_num, PATTERN_TEST, (u8)i);
+	}
+
+	/* External write, read and compare */
+	CHECK_STATUS(ddr3_tip_load_pattern_to_mem(dev_num, PATTERN_TEST));
+
+	CHECK_STATUS(ddr3_tip_reset_fifo_ptr(dev_num));
+
+	CHECK_STATUS(ddr3_tip_ext_read
+		     (dev_num, if_id,
+		      ((pattern_table[PATTERN_TEST].start_addr << 3) +
+		       ((SDRAM_CS_SIZE + 1) * effective_cs)), 1, read_pattern));
+
+	DEBUG_LEVELING(
+		DEBUG_LEVEL_TRACE,
+		("XSB-compt CS#%d: IF %d bus_id %d 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		 effective_cs, if_id, bus_id,
+		 read_pattern[0], read_pattern[1],
+		 read_pattern[2], read_pattern[3],
+		 read_pattern[4], read_pattern[5],
+		 read_pattern[6], read_pattern[7]));
+
+	/* compare byte per pup */
+	num_of_succ_byte_compare = 0;
+	for (word_in_pattern = start_xsb_offset;
+	     word_in_pattern < (TEST_PATTERN_LENGTH * num_of_word_mult);
+	     word_in_pattern++) {
+		word_offset = word_in_pattern;
+		if ((word_offset > (TEST_PATTERN_LENGTH * 2 - 1)))
+			continue;
+
+		if ((read_pattern[word_in_pattern] & pup_mask_table[bus_id]) ==
+		    (pattern_test_pattern_table[word_offset] &
+		     pup_mask_table[bus_id]))
+			num_of_succ_byte_compare++;
+	}
+
+	if ((TEST_PATTERN_LENGTH * num_of_word_mult - start_xsb_offset) ==
+	    num_of_succ_byte_compare) {
+		wr_supp_res[if_id][bus_id].stage = edge_offset;
+		DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+			       ("supplementary: shift to %d for if %d pup %d success\n",
+				edge_offset, if_id, bus_id));
+		wr_supp_res[if_id][bus_id].is_pup_fail = 0;
+
+		return MV_OK;
+	} else {
+		DEBUG_LEVELING(
+			DEBUG_LEVEL_TRACE,
+			("XSB-compt CS#%d: IF %d bus_id %d num_of_succ_byte_compare %d - Fail!\n",
+			 effective_cs, if_id, bus_id, num_of_succ_byte_compare));
+
+		DEBUG_LEVELING(
+			DEBUG_LEVEL_TRACE,
+			("XSB-compt: expected 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+			 pattern_test_pattern_table[0],
+			 pattern_test_pattern_table[1],
+			 pattern_test_pattern_table[2],
+			 pattern_test_pattern_table[3],
+			 pattern_test_pattern_table[4],
+			 pattern_test_pattern_table[5],
+			 pattern_test_pattern_table[6],
+			 pattern_test_pattern_table[7]));
+		DEBUG_LEVELING(
+			DEBUG_LEVEL_TRACE,
+			("XSB-compt: recieved 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+			 read_pattern[0], read_pattern[1],
+			 read_pattern[2], read_pattern[3],
+			 read_pattern[4], read_pattern[5],
+			 read_pattern[6], read_pattern[7]));
+
+		return MV_FAIL;
+	}
+}
+
+/*
+ * Dynamic write leveling sequence
+ */
+static int ddr3_tip_dynamic_write_leveling_seq(u32 dev_num)
+{
+	u32 bus_id, dq_id;
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      TRAINING_SW_2_REG, 0x1, 0x5));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      TRAINING_WL_REG, 0x50, 0xff));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      TRAINING_WL_REG, 0x5c, 0xff));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      GENERAL_TRAINING_OPCODE_REG, 0x381b82, 0x3c3faf));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      OPCODE_REG0_REG(1), (0x3 << 25), (0x3ffff << 9)));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      OPCODE_REG1_REG(1), 0x80, 0xffff));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      WL_DONE_CNTR_REF_REG, 0x14, 0xff));
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      TRAINING_WL_REG, 0xff5c, 0xffff));
+
+	/* mask PBS */
+	for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_dq_reg_map[dq_id], 0x1 << 24,
+			      0x1 << 24));
+	}
+
+	/* Mask all results */
+	for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_pup_reg_map[bus_id], 0x1 << 24,
+			      0x1 << 24));
+	}
+
+	/* Unmask only wanted */
+	for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_pup_reg_map[bus_id], 0, 0x1 << 24));
+	}
+
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      WL_DQS_PATTERN_REG, 0x1, 0x1));
+
+	return MV_OK;
+}
+
+/*
+ * Dynamic read leveling sequence
+ */
+static int ddr3_tip_dynamic_read_leveling_seq(u32 dev_num)
+{
+	u32 bus_id, dq_id;
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* mask PBS */
+	for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_dq_reg_map[dq_id], 0x1 << 24,
+			      0x1 << 24));
+	}
+
+	/* Mask all results */
+	for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_pup_reg_map[bus_id], 0x1 << 24,
+			      0x1 << 24));
+	}
+
+	/* Unmask only wanted */
+	for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_pup_reg_map[bus_id], 0, 0x1 << 24));
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Dynamic read leveling sequence
+ */
+static int ddr3_tip_dynamic_per_bit_read_leveling_seq(u32 dev_num)
+{
+	u32 bus_id, dq_id;
+	u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* mask PBS */
+	for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_dq_reg_map[dq_id], 0x1 << 24,
+			      0x1 << 24));
+	}
+
+	/* Mask all results */
+	for (bus_id = 0; bus_id < octets_per_if_num; bus_id++) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_pup_reg_map[bus_id], 0x1 << 24,
+			      0x1 << 24));
+	}
+
+	/* Unmask only wanted */
+	for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, dq_id / 8);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			      mask_results_dq_reg_map[dq_id], 0x0 << 24,
+			      0x1 << 24));
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Print write leveling supplementary results
+ */
+int ddr3_tip_print_wl_supp_result(u32 dev_num)
+{
+	u32 bus_id = 0, if_id = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	DEBUG_LEVELING(DEBUG_LEVEL_INFO,
+		       ("I/F0 PUP0 Result[0 - success, 1-fail] ...\n"));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0; bus_id < octets_per_if_num;
+		     bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			DEBUG_LEVELING(DEBUG_LEVEL_INFO,
+				       ("%d ,", wr_supp_res[if_id]
+					[bus_id].is_pup_fail));
+		}
+	}
+	DEBUG_LEVELING(
+		DEBUG_LEVEL_INFO,
+		("I/F0 PUP0 Stage[0-phase_shift, 1-clock_shift, 2-align_shift] ...\n"));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (bus_id = 0; bus_id < octets_per_if_num;
+		     bus_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, bus_id);
+			DEBUG_LEVELING(DEBUG_LEVEL_INFO,
+				       ("%d ,", wr_supp_res[if_id]
+					[bus_id].stage));
+		}
+	}
+
+	return MV_OK;
+}
+
+#define RD_FIFO_PTR_LOW_STAT_INDIR_ADDR		0x9a
+#define RD_FIFO_PTR_HIGH_STAT_INDIR_ADDR	0x9b
+/* position of falling dqs edge in fifo; walking 1 */
+#define RD_FIFO_DQS_FALL_EDGE_POS_0		0x1
+#define RD_FIFO_DQS_FALL_EDGE_POS_1		0x2
+#define RD_FIFO_DQS_FALL_EDGE_POS_2		0x4
+#define RD_FIFO_DQS_FALL_EDGE_POS_3		0x8
+#define RD_FIFO_DQS_FALL_EDGE_POS_4		0x10 /* lock */
+/* position of rising dqs edge in fifo; walking 0 */
+#define RD_FIFO_DQS_RISE_EDGE_POS_0		0x1fff
+#define RD_FIFO_DQS_RISE_EDGE_POS_1		0x3ffe
+#define RD_FIFO_DQS_RISE_EDGE_POS_2		0x3ffd
+#define RD_FIFO_DQS_RISE_EDGE_POS_3		0x3ffb
+#define RD_FIFO_DQS_RISE_EDGE_POS_4		0x3ff7 /* lock */
+#define TEST_ADDR		0x8
+#define TAPS_PER_UI		32
+#define UI_PER_RD_SAMPLE	4
+#define TAPS_PER_RD_SAMPLE	((UI_PER_RD_SAMPLE) * (TAPS_PER_UI))
+#define MAX_RD_SAMPLES		32
+#define MAX_RL_VALUE		((MAX_RD_SAMPLES) * (TAPS_PER_RD_SAMPLE))
+#define RD_FIFO_DLY		8
+#define STEP_SIZE		64
+#define RL_JITTER_WIDTH_LMT	20
+#define ADLL_TAPS_IN_CYCLE	64
+
+enum rl_dqs_burst_state {
+	RL_AHEAD = 0,
+	RL_INSIDE,
+	RL_BEHIND
+};
+
+
+int mv_ddr_rl_dqs_burst(u32 dev_num, u32 if_id, u32 freq)
+{
+	enum rl_dqs_burst_state rl_state[MAX_CS_NUM][MAX_BUS_NUM][MAX_INTERFACE_NUM] = { { {0} } };
+	enum hws_ddr_phy subphy_type = DDR_PHY_DATA;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	int cl_val = tm->interface_params[0].cas_l;
+	int rl_adll_val, rl_phase_val, sdr_cycle_incr, rd_sample, rd_ready;
+	int final_rd_sample, final_rd_ready;
+	int i, subphy_id, step;
+	int pass_lock_num = 0;
+	int init_pass_lock_num;
+	int phase_delta;
+	int min_phase, max_phase;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+	u32 rl_values[MAX_CS_NUM][MAX_BUS_NUM][MAX_INTERFACE_NUM] = { { {0} } };
+	u32 rl_min_values[MAX_CS_NUM][MAX_BUS_NUM][MAX_INTERFACE_NUM] = { { {0} } };
+	u32 rl_max_values[MAX_CS_NUM][MAX_BUS_NUM][MAX_INTERFACE_NUM] = { { {0} } };
+	u32 rl_val, rl_min_val[MAX_CS_NUM], rl_max_val[MAX_CS_NUM];
+	u32 reg_val_low, reg_val_high;
+	u32 reg_val, reg_mask;
+	uintptr_t test_addr = TEST_ADDR;
+
+
+	/* initialization */
+	if (mv_ddr_is_ecc_ena()) {
+		ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, TRAINING_SW_2_REG,
+				 &reg_val, MASK_ALL_BITS);
+		reg_mask = (TRAINING_ECC_MUX_MASK << TRAINING_ECC_MUX_OFFS) |
+			   (TRAINING_SW_OVRD_MASK << TRAINING_SW_OVRD_OFFS);
+		reg_val &= ~reg_mask;
+		reg_val |= (TRAINING_ECC_MUX_DIS << TRAINING_ECC_MUX_OFFS) |
+			   (TRAINING_SW_OVRD_ENA << TRAINING_SW_OVRD_OFFS);
+		ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, TRAINING_SW_2_REG,
+				  reg_val, MASK_ALL_BITS);
+		ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, TRAINING_REG,
+				 &reg_val, MASK_ALL_BITS);
+		reg_mask = (TRN_START_MASK << TRN_START_OFFS);
+		reg_val &= ~reg_mask;
+		reg_val |= TRN_START_ENA << TRN_START_OFFS;
+		ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, TRAINING_REG,
+				  reg_val, MASK_ALL_BITS);
+	}
+
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++)
+		for (subphy_id = 0; subphy_id < MAX_BUS_NUM; subphy_id++)
+			for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++)
+				if (IS_BUS_ACTIVE(tm->bus_act_mask, subphy_id) == 0)
+					pass_lock_num++; /* increment on inactive subphys */
+
+	init_pass_lock_num = pass_lock_num / max_cs;
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			training_result[training_stage][if_id] = TEST_SUCCESS;
+		}
+	}
+
+	/* search for dqs edges per subphy */
+	if_id = 0;
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+
+		pass_lock_num = init_pass_lock_num;
+		ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ODPG_DATA_CTRL_REG,
+				  effective_cs << ODPG_DATA_CS_OFFS,
+				  ODPG_DATA_CS_MASK << ODPG_DATA_CS_OFFS);
+		rl_min_val[effective_cs] = MAX_RL_VALUE;
+		rl_max_val[effective_cs] = 0;
+		step = STEP_SIZE;
+		for (i = 0; i < MAX_RL_VALUE; i += step) {
+			rl_val = 0;
+			sdr_cycle_incr = i / TAPS_PER_RD_SAMPLE; /* sdr cycle increment */
+			rd_sample = cl_val + 2 * sdr_cycle_incr;
+			/* fifo out to in delay in search is constant */
+			rd_ready = rd_sample + RD_FIFO_DLY;
+
+			ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, RD_DATA_SMPL_DLYS_REG,
+					  rd_sample << RD_SMPL_DLY_CS_OFFS(effective_cs),
+					  RD_SMPL_DLY_CS_MASK << RD_SMPL_DLY_CS_OFFS(effective_cs));
+			ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, RD_DATA_RDY_DLYS_REG,
+					  rd_ready << RD_RDY_DLY_CS_OFFS(effective_cs),
+					  RD_RDY_DLY_CS_MASK << RD_RDY_DLY_CS_OFFS(effective_cs));
+
+			/* one sdr (single data rate) cycle incremented on every four phases of ddr clock */
+			sdr_cycle_incr = i % TAPS_PER_RD_SAMPLE;
+			rl_adll_val = sdr_cycle_incr % MAX_RD_SAMPLES;
+			rl_phase_val = sdr_cycle_incr / MAX_RD_SAMPLES;
+			rl_val = ((rl_adll_val & RL_REF_DLY_MASK) << RL_REF_DLY_OFFS) |
+				 ((rl_phase_val & RL_PH_SEL_MASK) << RL_PH_SEL_OFFS);
+
+			/* write to all subphys (even to not connected or locked) */
+			ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST,
+					   0, DDR_PHY_DATA, RL_PHY_REG(effective_cs), rl_val);
+
+			/* reset read fifo assertion */
+			ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, if_id, SDRAM_CFG_REG,
+					  DATA_PUP_RD_RESET_ENA << DATA_PUP_RD_RESET_OFFS,
+					  DATA_PUP_RD_RESET_MASK << DATA_PUP_RD_RESET_OFFS);
+
+			/* reset read fifo deassertion */
+			ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, if_id, SDRAM_CFG_REG,
+					  DATA_PUP_RD_RESET_DIS << DATA_PUP_RD_RESET_OFFS,
+					  DATA_PUP_RD_RESET_MASK << DATA_PUP_RD_RESET_OFFS);
+
+			/* perform one read burst */
+			if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask))
+				readq(test_addr);
+			else
+				readl(test_addr);
+
+			/* progress read ptr; decide on rl state per byte */
+			for (subphy_id = 0; subphy_id < MAX_BUS_NUM; subphy_id++) {
+				if (rl_state[effective_cs][subphy_id][if_id] == RL_BEHIND)
+					continue; /* skip locked subphys */
+				ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, subphy_id, DDR_PHY_DATA,
+						  RD_FIFO_PTR_LOW_STAT_INDIR_ADDR, &reg_val_low);
+				ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, subphy_id, DDR_PHY_DATA,
+						  RD_FIFO_PTR_HIGH_STAT_INDIR_ADDR, &reg_val_high);
+				DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+					       ("%s: cs %d, step %d, subphy %d, state %d, low 0x%04x, high 0x%04x; move to ",
+						__func__, effective_cs, i, subphy_id,
+						rl_state[effective_cs][subphy_id][if_id],
+						reg_val_low, reg_val_high));
+
+				switch (rl_state[effective_cs][subphy_id][if_id]) {
+				case RL_AHEAD:
+					/* improve search resolution getting closer to the window */
+					if (reg_val_low == RD_FIFO_DQS_FALL_EDGE_POS_4 &&
+					    reg_val_high == RD_FIFO_DQS_RISE_EDGE_POS_4) {
+						rl_state[effective_cs][subphy_id][if_id] = RL_INSIDE;
+						rl_values[effective_cs][subphy_id][if_id] = i;
+						rl_min_values[effective_cs][subphy_id][if_id] = i;
+						DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+							       ("new state %d\n",
+								rl_state[effective_cs][subphy_id][if_id]));
+					} else if (reg_val_low == RD_FIFO_DQS_FALL_EDGE_POS_3 &&
+						   reg_val_high == RD_FIFO_DQS_RISE_EDGE_POS_3) {
+						step = (step < 2) ? step : 2;
+					} else if (reg_val_low == RD_FIFO_DQS_FALL_EDGE_POS_2 &&
+						   reg_val_high == RD_FIFO_DQS_RISE_EDGE_POS_2) {
+						step = (step < 16) ? step : 16;
+					} else if (reg_val_low == RD_FIFO_DQS_FALL_EDGE_POS_1 &&
+						   reg_val_high == RD_FIFO_DQS_RISE_EDGE_POS_1) {
+						step = (step < 32) ? step : 32;
+					} else if (reg_val_low == RD_FIFO_DQS_FALL_EDGE_POS_0 &&
+						   reg_val_high == RD_FIFO_DQS_RISE_EDGE_POS_0) {
+						step = (step < 64) ? step : 64;
+					} else {
+						/* otherwise, step is unchanged */
+					}
+					break;
+				case RL_INSIDE:
+					if (reg_val_low == RD_FIFO_DQS_FALL_EDGE_POS_4 &&
+					    reg_val_high == RD_FIFO_DQS_RISE_EDGE_POS_4) {
+						rl_max_values[effective_cs][subphy_id][if_id] = i;
+						if ((rl_max_values[effective_cs][subphy_id][if_id] -
+						     rl_min_values[effective_cs][subphy_id][if_id]) >
+						    ADLL_TAPS_IN_CYCLE) {
+							rl_state[effective_cs][subphy_id][if_id] = RL_BEHIND;
+							rl_values[effective_cs][subphy_id][if_id] =
+								(i + rl_values[effective_cs][subphy_id][if_id]) / 2;
+							pass_lock_num++;
+							DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+								       ("new lock %d\n", pass_lock_num));
+							if (rl_min_val[effective_cs] >
+							    rl_values[effective_cs][subphy_id][if_id])
+								rl_min_val[effective_cs] =
+									rl_values[effective_cs][subphy_id][if_id];
+							if (rl_max_val[effective_cs] <
+							    rl_values[effective_cs][subphy_id][if_id])
+								rl_max_val[effective_cs] =
+									rl_values[effective_cs][subphy_id][if_id];
+							step = 2;
+						}
+					}
+					if (reg_val_low != RD_FIFO_DQS_FALL_EDGE_POS_4 ||
+					    reg_val_high != RD_FIFO_DQS_RISE_EDGE_POS_4) {
+						if ((i - rl_values[effective_cs][subphy_id][if_id]) <
+						    RL_JITTER_WIDTH_LMT) {
+							/* inside the jitter; not valid segment */
+							rl_state[effective_cs][subphy_id][if_id] = RL_AHEAD;
+							DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+								       ("new state %d; jitter on mask\n",
+									rl_state[effective_cs][subphy_id][if_id]));
+						} else { /* finished valid segment */
+							rl_state[effective_cs][subphy_id][if_id] = RL_BEHIND;
+							rl_values[effective_cs][subphy_id][if_id] =
+								(i + rl_values[effective_cs][subphy_id][if_id]) / 2;
+							DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+								       ("new state %d, solution %d\n",
+									rl_state[effective_cs][subphy_id][if_id],
+									rl_values[effective_cs][subphy_id][if_id]));
+							pass_lock_num++;
+							DEBUG_LEVELING(DEBUG_LEVEL_TRACE,
+								       ("new lock %d\n", pass_lock_num));
+							if (rl_min_val[effective_cs] >
+							    rl_values[effective_cs][subphy_id][if_id])
+								rl_min_val[effective_cs] =
+									rl_values[effective_cs][subphy_id][if_id];
+							if (rl_max_val[effective_cs] <
+							    rl_values[effective_cs][subphy_id][if_id])
+								rl_max_val[effective_cs] =
+									rl_values[effective_cs][subphy_id][if_id];
+							step = 2;
+						}
+					}
+					break;
+				case RL_BEHIND: /* do nothing */
+					break;
+				}
+				DEBUG_LEVELING(DEBUG_LEVEL_TRACE, ("\n"));
+			}
+			DEBUG_LEVELING(DEBUG_LEVEL_TRACE, ("pass_lock_num %d\n", pass_lock_num));
+			/* exit condition */
+			if (pass_lock_num == MAX_BUS_NUM)
+				break;
+		} /* for-loop on i */
+
+		if (pass_lock_num != MAX_BUS_NUM) {
+			DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+				       ("%s: cs %d, pass_lock_num %d, max_bus_num %d, init_pass_lock_num %d\n",
+				       __func__, effective_cs, pass_lock_num, MAX_BUS_NUM, init_pass_lock_num));
+			for (subphy_id = 0; subphy_id < MAX_BUS_NUM; subphy_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_id);
+				DEBUG_LEVELING(DEBUG_LEVEL_ERROR,
+					       ("%s: subphy %d %s\n",
+						__func__, subphy_id,
+						(rl_state[effective_cs][subphy_id][if_id] == RL_BEHIND) ?
+						"locked" : "not locked"));
+			}
+		}
+	} /* for-loop on effective_cs */
+
+	/* post-processing read leveling results */
+	if_id = 0;
+	for (effective_cs = 0; effective_cs < max_cs; effective_cs++) {
+		phase_delta = 0;
+		i = rl_min_val[effective_cs];
+		sdr_cycle_incr = i / TAPS_PER_RD_SAMPLE; /* sdr cycle increment */
+		rd_sample = cl_val + 2 * sdr_cycle_incr;
+		rd_ready = rd_sample + RD_FIFO_DLY;
+		min_phase = (rl_min_val[effective_cs] - (sdr_cycle_incr * TAPS_PER_RD_SAMPLE)) % MAX_RD_SAMPLES;
+		max_phase = (rl_max_val[effective_cs] - (sdr_cycle_incr * TAPS_PER_RD_SAMPLE)) % MAX_RD_SAMPLES;
+		final_rd_sample = rd_sample;
+		final_rd_ready = rd_ready;
+
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, RD_DATA_SMPL_DLYS_REG,
+				  rd_sample << RD_SMPL_DLY_CS_OFFS(effective_cs),
+				  RD_SMPL_DLY_CS_MASK << RD_SMPL_DLY_CS_OFFS(effective_cs));
+		ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, RD_DATA_RDY_DLYS_REG,
+				  rd_ready << RD_RDY_DLY_CS_OFFS(effective_cs),
+				  RD_RDY_DLY_CS_MASK << RD_RDY_DLY_CS_OFFS(effective_cs));
+		DEBUG_LEVELING(DEBUG_LEVEL_INFO,
+			       ("%s: cs %d, min phase %d, max phase %d, read sample %d\n",
+				__func__, effective_cs, min_phase, max_phase, rd_sample));
+
+		for (subphy_id = 0; subphy_id < MAX_BUS_NUM; subphy_id++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_id);
+			/* reduce sdr cycle per cs; extract rl adll and phase values */
+			i = rl_values[effective_cs][subphy_id][if_id] - (sdr_cycle_incr * TAPS_PER_RD_SAMPLE);
+			rl_adll_val = i % MAX_RD_SAMPLES;
+			rl_phase_val = i / MAX_RD_SAMPLES;
+			rl_phase_val -= phase_delta;
+			DEBUG_LEVELING(DEBUG_LEVEL_INFO,
+				       ("%s: final results: cs %d, subphy %d, read sample %d read ready %d, rl_phase_val %d, rl_adll_val %d\n",
+					__func__, effective_cs, subphy_id, final_rd_sample,
+					final_rd_ready, rl_phase_val, rl_adll_val));
+
+			rl_val = ((rl_adll_val & RL_REF_DLY_MASK) << RL_REF_DLY_OFFS) |
+				 ((rl_phase_val & RL_PH_SEL_MASK) << RL_PH_SEL_OFFS);
+			ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ACCESS_TYPE_UNICAST,
+					   subphy_id, subphy_type, RL_PHY_REG(effective_cs), rl_val);
+		}
+	} /* for-loop on effective cs */
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		if (odt_config != 0)
+			CHECK_STATUS(ddr3_tip_write_additional_odt_setting(dev_num, if_id));
+	}
+
+
+	/* reset read fifo assertion */
+	ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, if_id, SDRAM_CFG_REG,
+			  DATA_PUP_RD_RESET_ENA << DATA_PUP_RD_RESET_OFFS,
+			  DATA_PUP_RD_RESET_MASK << DATA_PUP_RD_RESET_OFFS);
+
+	/* reset read fifo deassertion */
+	ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, if_id, SDRAM_CFG_REG,
+			  DATA_PUP_RD_RESET_DIS << DATA_PUP_RD_RESET_OFFS,
+			  DATA_PUP_RD_RESET_MASK << DATA_PUP_RD_RESET_OFFS);
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_leveling.h b/drivers/ddr/marvell/a38x/ddr3_training_leveling.h
new file mode 100644
index 0000000..6719fb8
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_leveling.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR3_TRAINING_LEVELING_H_
+#define _DDR3_TRAINING_LEVELING_H_
+
+#define MAX_DQ_READ_LEVELING_DELAY 15
+
+int ddr3_tip_print_wl_supp_result(u32 dev_num);
+int ddr3_tip_calc_cs_mask(u32 dev_num, u32 if_id, u32 effective_cs,
+			  u32 *cs_mask);
+
+#endif /* _DDR3_TRAINING_LEVELING_H_ */
diff --git a/drivers/ddr/marvell/a38x/ddr3_training_pbs.c b/drivers/ddr/marvell/a38x/ddr3_training_pbs.c
new file mode 100644
index 0000000..b7dfebd
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr3_training_pbs.c
@@ -0,0 +1,1012 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_training_db.h"
+#include "mv_ddr_common.h"
+#include "mv_ddr_regs.h"
+
+#define TYPICAL_PBS_VALUE	12
+
+u32 nominal_adll[MAX_INTERFACE_NUM * MAX_BUS_NUM];
+enum hws_training_ip_stat train_status[MAX_INTERFACE_NUM];
+u8 result_mat[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS];
+u8 result_mat_rx_dqs[MAX_INTERFACE_NUM][MAX_BUS_NUM][MAX_CS_NUM];
+/* 4-EEWA, 3-EWA, 2-SWA, 1-Fail, 0-Pass */
+u8 result_all_bit[MAX_BUS_NUM * BUS_WIDTH_IN_BITS * MAX_INTERFACE_NUM];
+u8 max_pbs_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 min_pbs_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 max_adll_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 min_adll_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u32 pbsdelay_per_pup[NUM_OF_PBS_MODES][MAX_INTERFACE_NUM][MAX_BUS_NUM][MAX_CS_NUM];
+u8 adll_shift_lock[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+u8 adll_shift_val[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+enum hws_pattern pbs_pattern = PATTERN_VREF;
+static u8 pup_state[MAX_INTERFACE_NUM][MAX_BUS_NUM];
+
+/*
+ * Name:     ddr3_tip_pbs
+ * Desc:     PBS
+ * Args:     TBD
+ * Notes:
+ * Returns:  OK if success, other error code if fail.
+ */
+int ddr3_tip_pbs(u32 dev_num, enum pbs_dir pbs_mode)
+{
+	u32 res0[MAX_INTERFACE_NUM];
+	int adll_tap = MEGA / mv_ddr_freq_get(medium_freq) / 64;
+	int pad_num = 0;
+	enum hws_search_dir search_dir =
+		(pbs_mode == PBS_RX_MODE) ? HWS_HIGH2LOW : HWS_LOW2HIGH;
+	enum hws_dir dir = (pbs_mode == PBS_RX_MODE) ? OPER_READ : OPER_WRITE;
+	int iterations = (pbs_mode == PBS_RX_MODE) ? 31 : 63;
+	u32 res_valid_mask = (pbs_mode == PBS_RX_MODE) ? 0x1f : 0x3f;
+	int init_val = (search_dir == HWS_LOW2HIGH) ? 0 : iterations;
+	enum hws_edge_compare search_edge = EDGE_FP;
+	u32 pup = 0, bit = 0, if_id = 0, all_lock = 0, cs_num = 0;
+	u32 reg_addr = 0;
+	u32 validation_val = 0;
+	u32 cs_enable_reg_val[MAX_INTERFACE_NUM];
+	u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
+	u8 temp = 0;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* save current cs enable reg val */
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		/* save current cs enable reg val */
+		CHECK_STATUS(ddr3_tip_if_read
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val, MASK_ALL_BITS));
+
+		/* enable single cs */
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, (1 << 3), (1 << 3)));
+	}
+
+	reg_addr = (pbs_mode == PBS_RX_MODE) ?
+		CRX_PHY_REG(effective_cs) :
+		CTX_PHY_REG(effective_cs);
+	ddr3_tip_read_adll_value(dev_num, nominal_adll, reg_addr, MASK_ALL_BITS);
+
+	/* stage 1 shift ADLL */
+	ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST,
+			     PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST,
+			     PARAM_NOT_CARE, RESULT_PER_BIT,
+			     HWS_CONTROL_ELEMENT_ADLL, search_dir, dir,
+			     tm->if_act_mask, init_val, iterations,
+			     pbs_pattern, search_edge, CS_SINGLE, cs_num,
+			     train_status);
+	validation_val = (pbs_mode == PBS_RX_MODE) ? 0x1f : 0;
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			min_adll_per_pup[if_id][pup] =
+				(pbs_mode == PBS_RX_MODE) ? 0x1f : 0x3f;
+			pup_state[if_id][pup] = 0x3;
+			adll_shift_lock[if_id][pup] = 1;
+			max_adll_per_pup[if_id][pup] = 0x0;
+		}
+	}
+
+	/* EBA */
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+			CHECK_STATUS(ddr3_tip_if_read
+				     (dev_num, ACCESS_TYPE_MULTICAST,
+				      PARAM_NOT_CARE,
+				      mask_results_dq_reg_map[
+					      bit + pup * BUS_WIDTH_IN_BITS],
+				      res0, MASK_ALL_BITS));
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+						 ("FP I/F %d, bit:%d, pup:%d res0 0x%x\n",
+						  if_id, bit, pup,
+						  res0[if_id]));
+				if (pup_state[if_id][pup] != 3)
+					continue;
+				/* if not EBA state than move to next pup */
+
+				if ((res0[if_id] & 0x2000000) == 0) {
+					DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+							 ("-- Fail Training IP\n"));
+					/* training machine failed */
+					pup_state[if_id][pup] = 1;
+					adll_shift_lock[if_id][pup] = 0;
+					continue;
+				}
+
+				else if ((res0[if_id] & res_valid_mask) ==
+					 validation_val) {
+					DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+							 ("-- FAIL EBA %d %d %d %d\n",
+							  if_id, bit, pup,
+							  res0[if_id]));
+					pup_state[if_id][pup] = 4;
+					/* this pup move to EEBA */
+					adll_shift_lock[if_id][pup] = 0;
+					continue;
+				} else {
+					/*
+					 * The search ended in Pass we need
+					 * Fail
+					 */
+					res0[if_id] =
+						(pbs_mode == PBS_RX_MODE) ?
+						((res0[if_id] &
+						  res_valid_mask) + 1) :
+						((res0[if_id] &
+						  res_valid_mask) - 1);
+					max_adll_per_pup[if_id][pup] =
+						(max_adll_per_pup[if_id][pup] <
+						 res0[if_id]) ?
+						(u8)res0[if_id] :
+						max_adll_per_pup[if_id][pup];
+					min_adll_per_pup[if_id][pup] =
+						(res0[if_id] >
+						 min_adll_per_pup[if_id][pup]) ?
+						min_adll_per_pup[if_id][pup] :
+						(u8)
+						res0[if_id];
+					/*
+					 * vs the Rx we are searching for the
+					 * smallest value of DQ shift so all
+					 * Bus would fail
+					 */
+					adll_shift_val[if_id][pup] =
+						(pbs_mode == PBS_RX_MODE) ?
+						max_adll_per_pup[if_id][pup] :
+						min_adll_per_pup[if_id][pup];
+				}
+			}
+		}
+	}
+
+	/* EEBA */
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+			if (pup_state[if_id][pup] != 4)
+				continue;
+			/*
+			 * if pup state different from EEBA than move to
+			 * next pup
+			 */
+			reg_addr = (pbs_mode == PBS_RX_MODE) ?
+				(0x54 + effective_cs * 0x10) :
+				(0x14 + effective_cs * 0x10);
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA,
+				      reg_addr, 0x1f));
+			reg_addr = (pbs_mode == PBS_RX_MODE) ?
+				(0x55 + effective_cs * 0x10) :
+				(0x15 + effective_cs * 0x10);
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA,
+				      reg_addr, 0x1f));
+			/* initialize the Edge2 Max. */
+			adll_shift_val[if_id][pup] = 0;
+			min_adll_per_pup[if_id][pup] =
+				(pbs_mode == PBS_RX_MODE) ? 0x1f : 0x3f;
+			max_adll_per_pup[if_id][pup] = 0x0;
+
+			ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST,
+					     PARAM_NOT_CARE,
+					     ACCESS_TYPE_MULTICAST,
+					     PARAM_NOT_CARE, RESULT_PER_BIT,
+					     HWS_CONTROL_ELEMENT_ADLL,
+					     search_dir, dir,
+					     tm->if_act_mask, init_val,
+					     iterations, pbs_pattern,
+					     search_edge, CS_SINGLE, cs_num,
+					     train_status);
+			DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+					 ("ADLL shift results:\n"));
+
+			for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+				CHECK_STATUS(ddr3_tip_if_read
+					     (dev_num, ACCESS_TYPE_MULTICAST,
+					      PARAM_NOT_CARE,
+					      mask_results_dq_reg_map[
+						      bit + pup *
+						      BUS_WIDTH_IN_BITS],
+					      res0, MASK_ALL_BITS));
+				DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+						 ("FP I/F %d, bit:%d, pup:%d res0 0x%x\n",
+						  if_id, bit, pup,
+						  res0[if_id]));
+
+				if ((res0[if_id] & 0x2000000) == 0) {
+					DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+							 (" -- EEBA Fail\n"));
+					bit = BUS_WIDTH_IN_BITS;
+					/* exit bit loop */
+					DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+							 ("-- EEBA Fail Training IP\n"));
+					/*
+					 * training machine failed but pass
+					 * before in the EBA so maybe the DQS
+					 * shift change env.
+					 */
+					pup_state[if_id][pup] = 2;
+					adll_shift_lock[if_id][pup] = 0;
+					reg_addr = (pbs_mode == PBS_RX_MODE) ?
+						(0x54 + effective_cs * 0x10) :
+						(0x14 + effective_cs * 0x10);
+					CHECK_STATUS(ddr3_tip_bus_write
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      ACCESS_TYPE_UNICAST, pup,
+						      DDR_PHY_DATA, reg_addr,
+						      0x0));
+					reg_addr = (pbs_mode == PBS_RX_MODE) ?
+						(0x55 + effective_cs * 0x10) :
+						(0x15 + effective_cs * 0x10);
+					CHECK_STATUS(ddr3_tip_bus_write
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      ACCESS_TYPE_UNICAST, pup,
+						      DDR_PHY_DATA, reg_addr,
+						      0x0));
+					continue;
+				} else if ((res0[if_id] & res_valid_mask) ==
+					   validation_val) {
+					/* exit bit loop */
+					bit = BUS_WIDTH_IN_BITS;
+					DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+							 ("-- FAIL EEBA\n"));
+					/* this pup move to SBA */
+					pup_state[if_id][pup] = 2;
+					adll_shift_lock[if_id][pup] = 0;
+					reg_addr = (pbs_mode == PBS_RX_MODE) ?
+						(0x54 + effective_cs * 0x10) :
+						(0x14 + effective_cs * 0x10);
+					CHECK_STATUS(ddr3_tip_bus_write
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      ACCESS_TYPE_UNICAST, pup,
+						      DDR_PHY_DATA, reg_addr,
+						      0x0));
+					reg_addr = (pbs_mode == PBS_RX_MODE) ?
+						(0x55 + effective_cs * 0x10) :
+						(0x15 + effective_cs * 0x10);
+					CHECK_STATUS(ddr3_tip_bus_write
+						     (dev_num,
+						      ACCESS_TYPE_UNICAST,
+						      if_id,
+						      ACCESS_TYPE_UNICAST, pup,
+						      DDR_PHY_DATA, reg_addr,
+						      0x0));
+					continue;
+				} else {
+					adll_shift_lock[if_id][pup] = 1;
+					/*
+					 * The search ended in Pass we need
+					 * Fail
+					 */
+					res0[if_id] =
+						(pbs_mode == PBS_RX_MODE) ?
+						((res0[if_id] &
+						  res_valid_mask) + 1) :
+						((res0[if_id] &
+						  res_valid_mask) - 1);
+					max_adll_per_pup[if_id][pup] =
+						(max_adll_per_pup[if_id][pup] <
+						 res0[if_id]) ?
+						(u8)res0[if_id] :
+						max_adll_per_pup[if_id][pup];
+					min_adll_per_pup[if_id][pup] =
+						(res0[if_id] >
+						 min_adll_per_pup[if_id][pup]) ?
+						min_adll_per_pup[if_id][pup] :
+						(u8)res0[if_id];
+					/*
+					 * vs the Rx we are searching for the
+					 * smallest value of DQ shift so all Bus
+					 * would fail
+					 */
+					adll_shift_val[if_id][pup] =
+						(pbs_mode == PBS_RX_MODE) ?
+						max_adll_per_pup[if_id][pup] :
+						min_adll_per_pup[if_id][pup];
+				}
+			}
+		}
+	}
+
+	/* Print Stage result */
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+					 ("FP I/F %d, ADLL Shift for EBA: pup[%d] Lock status = %d Lock Val = %d,%d\n",
+					  if_id, pup,
+					  adll_shift_lock[if_id][pup],
+					  max_adll_per_pup[if_id][pup],
+					  min_adll_per_pup[if_id][pup]));
+		}
+	}
+	DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+			 ("Update ADLL Shift of all pups:\n"));
+
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			if (adll_shift_lock[if_id][pup] != 1)
+				continue;
+			/* if pup not locked continue to next pup */
+
+			reg_addr = (pbs_mode == PBS_RX_MODE) ?
+				(0x3 + effective_cs * 4) :
+				(0x1 + effective_cs * 4);
+			CHECK_STATUS(ddr3_tip_bus_write
+				     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+				      ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA,
+				      reg_addr, adll_shift_val[if_id][pup]));
+			DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE,
+					 ("FP I/F %d, Pup[%d] = %d\n", if_id,
+					  pup, adll_shift_val[if_id][pup]));
+		}
+	}
+
+	/* PBS EEBA&EBA */
+	/* Start the Per Bit Skew search */
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			max_pbs_per_pup[if_id][pup] = 0x0;
+			min_pbs_per_pup[if_id][pup] = 0x1f;
+			for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+				/* reset result for PBS */
+				result_all_bit[bit + pup * BUS_WIDTH_IN_BITS +
+					       if_id * MAX_BUS_NUM *
+					       BUS_WIDTH_IN_BITS] = 0;
+			}
+		}
+	}
+
+	iterations = 31;
+	search_dir = HWS_LOW2HIGH;
+	/* !!!!! ran sh (search_dir == HWS_LOW2HIGH)?0:iterations; */
+	init_val = 0;
+
+	ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			     ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+			     RESULT_PER_BIT, HWS_CONTROL_ELEMENT_DQ_SKEW,
+			     search_dir, dir, tm->if_act_mask, init_val,
+			     iterations, pbs_pattern, search_edge,
+			     CS_SINGLE, cs_num, train_status);
+
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			if (adll_shift_lock[if_id][pup] != 1) {
+				/* if pup not lock continue to next pup */
+				continue;
+			}
+
+			for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+				CHECK_STATUS(ddr3_tip_if_read
+					     (dev_num, ACCESS_TYPE_MULTICAST,
+					      PARAM_NOT_CARE,
+					      mask_results_dq_reg_map[
+						      bit +
+						      pup * BUS_WIDTH_IN_BITS],
+					      res0, MASK_ALL_BITS));
+				DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+						 ("Per Bit Skew search, FP I/F %d, bit:%d, pup:%d res0 0x%x\n",
+						  if_id, bit, pup,
+						  res0[if_id]));
+				if ((res0[if_id] & 0x2000000) == 0) {
+					DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+							 ("--EBA PBS Fail - Training IP machine\n"));
+					/* exit the bit loop */
+					bit = BUS_WIDTH_IN_BITS;
+					/*
+					 * ADLL is no long in lock need new
+					 * search
+					 */
+					adll_shift_lock[if_id][pup] = 0;
+					/* Move to SBA */
+					pup_state[if_id][pup] = 2;
+					max_pbs_per_pup[if_id][pup] = 0x0;
+					min_pbs_per_pup[if_id][pup] = 0x1f;
+					continue;
+				} else {
+					temp = (u8)(res0[if_id] &
+						    res_valid_mask);
+					max_pbs_per_pup[if_id][pup] =
+						(temp >
+						 max_pbs_per_pup[if_id][pup]) ?
+						temp :
+						max_pbs_per_pup[if_id][pup];
+					min_pbs_per_pup[if_id][pup] =
+						(temp <
+						 min_pbs_per_pup[if_id][pup]) ?
+						temp :
+						min_pbs_per_pup[if_id][pup];
+					result_all_bit[bit +
+						       pup * BUS_WIDTH_IN_BITS +
+						       if_id * MAX_BUS_NUM *
+						       BUS_WIDTH_IN_BITS] =
+						temp;
+				}
+			}
+		}
+	}
+
+	/* Check all Pup lock */
+	all_lock = 1;
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			all_lock = all_lock * adll_shift_lock[if_id][pup];
+		}
+	}
+
+	/* Only if not all Pups Lock */
+	if (all_lock == 0) {
+		DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+				 ("##########ADLL shift for SBA###########\n"));
+
+		/* ADLL shift for SBA */
+		search_dir = (pbs_mode == PBS_RX_MODE) ? HWS_LOW2HIGH :
+			HWS_HIGH2LOW;
+		init_val = (search_dir == HWS_LOW2HIGH) ? 0 : iterations;
+		for (pup = 0; pup < octets_per_if_num; pup++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				if (adll_shift_lock[if_id][pup] == 1) {
+					/*if pup lock continue to next pup */
+					continue;
+				}
+				/*init the var altogth init before */
+				adll_shift_lock[if_id][pup] = 0;
+				reg_addr = (pbs_mode == PBS_RX_MODE) ?
+					(0x54 + effective_cs * 0x10) :
+					(0x14 + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr, 0));
+				reg_addr = (pbs_mode == PBS_RX_MODE) ?
+					(0x55 + effective_cs * 0x10) :
+					(0x15 + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr, 0));
+				reg_addr = (pbs_mode == PBS_RX_MODE) ?
+					(0x5f + effective_cs * 0x10) :
+					(0x1f + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr, 0));
+				/* initilaze the Edge2 Max. */
+				adll_shift_val[if_id][pup] = 0;
+				min_adll_per_pup[if_id][pup] = 0x1f;
+				max_adll_per_pup[if_id][pup] = 0x0;
+
+				ddr3_tip_ip_training(dev_num,
+						     ACCESS_TYPE_MULTICAST,
+						     PARAM_NOT_CARE,
+						     ACCESS_TYPE_MULTICAST,
+						     PARAM_NOT_CARE,
+						     RESULT_PER_BIT,
+						     HWS_CONTROL_ELEMENT_ADLL,
+						     search_dir, dir,
+						     tm->if_act_mask,
+						     init_val, iterations,
+						     pbs_pattern,
+						     search_edge, CS_SINGLE,
+						     cs_num, train_status);
+
+				for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+					CHECK_STATUS(ddr3_tip_if_read
+						     (dev_num,
+						      ACCESS_TYPE_MULTICAST,
+						      PARAM_NOT_CARE,
+						      mask_results_dq_reg_map
+						      [bit +
+						       pup *
+						       BUS_WIDTH_IN_BITS],
+						      res0, MASK_ALL_BITS));
+					DEBUG_PBS_ENGINE(
+						DEBUG_LEVEL_INFO,
+						("FP I/F %d, bit:%d, pup:%d res0 0x%x\n",
+						 if_id, bit, pup, res0[if_id]));
+					if ((res0[if_id] & 0x2000000) == 0) {
+						/* exit the bit loop */
+						bit = BUS_WIDTH_IN_BITS;
+						/* Fail SBA --> Fail PBS */
+						pup_state[if_id][pup] = 1;
+						DEBUG_PBS_ENGINE
+							(DEBUG_LEVEL_INFO,
+							 (" SBA Fail\n"));
+						continue;
+					} else {
+						/*
+						 * - increment to get all
+						 * 8 bit lock.
+						 */
+						adll_shift_lock[if_id][pup]++;
+						/*
+						 * The search ended in Pass
+						 * we need Fail
+						 */
+						res0[if_id] =
+							(pbs_mode == PBS_RX_MODE) ?
+							((res0[if_id] & res_valid_mask) + 1) :
+							((res0[if_id] & res_valid_mask) - 1);
+						max_adll_per_pup[if_id][pup] =
+							(max_adll_per_pup[if_id]
+							 [pup] < res0[if_id]) ?
+							(u8)res0[if_id] :
+							max_adll_per_pup[if_id][pup];
+						min_adll_per_pup[if_id][pup] =
+							(res0[if_id] >
+							 min_adll_per_pup[if_id]
+							 [pup]) ?
+							min_adll_per_pup[if_id][pup] :
+							(u8)res0[if_id];
+						/*
+						 * vs the Rx we are searching for
+						 * the smallest value of DQ shift
+						 * so all Bus would fail
+						 */
+						adll_shift_val[if_id][pup] =
+							(pbs_mode == PBS_RX_MODE) ?
+							max_adll_per_pup[if_id][pup] :
+							min_adll_per_pup[if_id][pup];
+					}
+				}
+				/* 1 is lock */
+				adll_shift_lock[if_id][pup] =
+					(adll_shift_lock[if_id][pup] == 8) ?
+					1 : 0;
+				reg_addr = (pbs_mode == PBS_RX_MODE) ?
+					(0x3 + effective_cs * 4) :
+					(0x1 + effective_cs * 4);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr,
+					      adll_shift_val[if_id][pup]));
+				DEBUG_PBS_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("adll_shift_lock[%x][%x] = %x\n",
+					 if_id, pup,
+					 adll_shift_lock[if_id][pup]));
+			}
+		}
+
+		/* End ADLL Shift for SBA */
+		/* Start the Per Bit Skew search */
+		/* The ADLL shift finished with a Pass */
+		search_edge = (pbs_mode == PBS_RX_MODE) ? EDGE_PF : EDGE_FP;
+		search_dir = (pbs_mode == PBS_RX_MODE) ?
+			HWS_LOW2HIGH : HWS_HIGH2LOW;
+		iterations = 0x1f;
+		/* - The initial value is different in Rx and Tx mode */
+		init_val = (pbs_mode == PBS_RX_MODE) ? 0 : iterations;
+
+		ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST,
+				     PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST,
+				     PARAM_NOT_CARE, RESULT_PER_BIT,
+				     HWS_CONTROL_ELEMENT_DQ_SKEW,
+				     search_dir, dir, tm->if_act_mask,
+				     init_val, iterations, pbs_pattern,
+				     search_edge, CS_SINGLE, cs_num,
+				     train_status);
+
+		for (pup = 0; pup < octets_per_if_num; pup++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+					CHECK_STATUS(ddr3_tip_if_read
+						     (dev_num,
+						      ACCESS_TYPE_MULTICAST,
+						      PARAM_NOT_CARE,
+						      mask_results_dq_reg_map
+						      [bit +
+						       pup *
+						       BUS_WIDTH_IN_BITS],
+						      res0, MASK_ALL_BITS));
+					if (pup_state[if_id][pup] != 2) {
+						/*
+						 * if pup is not SBA continue
+						 * to next pup
+						 */
+						bit = BUS_WIDTH_IN_BITS;
+						continue;
+					}
+					DEBUG_PBS_ENGINE(
+						DEBUG_LEVEL_INFO,
+						("Per Bit Skew search, PF I/F %d, bit:%d, pup:%d res0 0x%x\n",
+						 if_id, bit, pup, res0[if_id]));
+					if ((res0[if_id] & 0x2000000) == 0) {
+						DEBUG_PBS_ENGINE
+							(DEBUG_LEVEL_INFO,
+							 ("SBA Fail\n"));
+
+						max_pbs_per_pup[if_id][pup] =
+							0x1f;
+						result_all_bit[
+							bit + pup *
+							BUS_WIDTH_IN_BITS +
+							if_id * MAX_BUS_NUM *
+							BUS_WIDTH_IN_BITS] =
+							0x1f;
+					} else {
+						temp = (u8)(res0[if_id] &
+							    res_valid_mask);
+						max_pbs_per_pup[if_id][pup] =
+							(temp >
+							 max_pbs_per_pup[if_id]
+							 [pup]) ? temp :
+							max_pbs_per_pup
+							[if_id][pup];
+						min_pbs_per_pup[if_id][pup] =
+							(temp <
+							 min_pbs_per_pup[if_id]
+							 [pup]) ? temp :
+							min_pbs_per_pup
+							[if_id][pup];
+						result_all_bit[
+							bit + pup *
+							BUS_WIDTH_IN_BITS +
+							if_id * MAX_BUS_NUM *
+							BUS_WIDTH_IN_BITS] =
+							temp;
+						adll_shift_lock[if_id][pup] = 1;
+					}
+				}
+			}
+		}
+
+		/* Check all Pup state */
+		all_lock = 1;
+		for (pup = 0; pup < octets_per_if_num; pup++) {
+			/*
+			 * DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+			 * ("pup_state[%d][%d] = %d\n",if_id,pup,pup_state
+			 * [if_id][pup]));
+			*/
+		}
+	}
+
+	/* END OF SBA */
+	/* Norm */
+	for (pup = 0; pup < octets_per_if_num; pup++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+		for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+			for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
+			     if_id++) {
+				VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+				/* if pup not lock continue to next pup */
+				if (adll_shift_lock[if_id][pup] != 1) {
+					DEBUG_PBS_ENGINE(
+						DEBUG_LEVEL_ERROR,
+						("PBS failed for IF #%d\n",
+						 if_id));
+					training_result[training_stage][if_id]
+						= TEST_FAILED;
+
+					result_mat[if_id][pup][bit] = 0;
+					max_pbs_per_pup[if_id][pup] = 0;
+					min_pbs_per_pup[if_id][pup] = 0;
+				} else {
+					training_result[
+						training_stage][if_id] =
+						(training_result[training_stage]
+						 [if_id] == TEST_FAILED) ?
+						TEST_FAILED : TEST_SUCCESS;
+					result_mat[if_id][pup][bit] =
+						result_all_bit[
+							bit + pup *
+							BUS_WIDTH_IN_BITS +
+							if_id * MAX_BUS_NUM *
+							BUS_WIDTH_IN_BITS] -
+						min_pbs_per_pup[if_id][pup];
+				}
+				DEBUG_PBS_ENGINE(
+					DEBUG_LEVEL_INFO,
+					("The abs min_pbs[%d][%d] = %d\n",
+					 if_id, pup,
+					 min_pbs_per_pup[if_id][pup]));
+			}
+		}
+	}
+
+	/* Clean all results */
+	ddr3_tip_clean_pbs_result(dev_num, pbs_mode);
+
+	/* DQ PBS register update with the final result */
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup = 0; pup < octets_per_if_num; pup++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+
+			DEBUG_PBS_ENGINE(
+				DEBUG_LEVEL_INFO,
+				("Final Results: if_id %d, pup %d, Pup State: %d\n",
+				 if_id, pup, pup_state[if_id][pup]));
+			for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+				if (dq_map_table == NULL) {
+					DEBUG_PBS_ENGINE(
+						DEBUG_LEVEL_ERROR,
+						("dq_map_table not initialized\n"));
+					return MV_FAIL;
+				}
+				pad_num = dq_map_table[
+					bit + pup * BUS_WIDTH_IN_BITS +
+					if_id * BUS_WIDTH_IN_BITS *
+					MAX_BUS_NUM];
+				DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO,
+						 ("result_mat: %d ",
+						  result_mat[if_id][pup]
+						  [bit]));
+				reg_addr = (pbs_mode == PBS_RX_MODE) ?
+					PBS_RX_PHY_REG(effective_cs, 0) :
+					PBS_TX_PHY_REG(effective_cs, 0);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr + pad_num,
+					      result_mat[if_id][pup][bit]));
+			}
+
+			if (max_pbs_per_pup[if_id][pup] == min_pbs_per_pup[if_id][pup]) {
+				temp = TYPICAL_PBS_VALUE;
+			} else {
+				temp = ((max_adll_per_pup[if_id][pup] -
+					 min_adll_per_pup[if_id][pup]) *
+					adll_tap /
+					(max_pbs_per_pup[if_id][pup] -
+					 min_pbs_per_pup[if_id][pup]));
+			}
+			pbsdelay_per_pup[pbs_mode]
+			[if_id][pup][effective_cs] = temp;
+
+			/* RX results ready, write RX also */
+			if (pbs_mode == PBS_TX_MODE) {
+				/* Write TX results */
+				reg_addr = (0x14 + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr,
+					      (max_pbs_per_pup[if_id][pup] -
+					       min_pbs_per_pup[if_id][pup]) /
+					      2));
+				reg_addr = (0x15 + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr,
+					      (max_pbs_per_pup[if_id][pup] -
+					       min_pbs_per_pup[if_id][pup]) /
+					      2));
+
+				/* Write previously stored RX results */
+				reg_addr = (0x54 + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr,
+					      result_mat_rx_dqs[if_id][pup]
+					      [effective_cs]));
+				reg_addr = (0x55 + effective_cs * 0x10);
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr,
+					      result_mat_rx_dqs[if_id][pup]
+					      [effective_cs]));
+			} else {
+				/*
+				 * RX results may affect RL results correctess,
+				 * so just store the results that will written
+				 * in TX stage
+				 */
+				result_mat_rx_dqs[if_id][pup][effective_cs] =
+					(max_pbs_per_pup[if_id][pup] -
+					 min_pbs_per_pup[if_id][pup]) / 2;
+			}
+			DEBUG_PBS_ENGINE(
+				DEBUG_LEVEL_INFO,
+				(", PBS tap=%d [psec] ==> skew observed = %d\n",
+				 temp,
+				 ((max_pbs_per_pup[if_id][pup] -
+				   min_pbs_per_pup[if_id][pup]) *
+				 temp)));
+		}
+	}
+
+	/* Write back to the phy the default values */
+	reg_addr = (pbs_mode == PBS_RX_MODE) ?
+		CRX_PHY_REG(effective_cs) :
+		CTX_PHY_REG(effective_cs);
+	ddr3_tip_write_adll_value(dev_num, nominal_adll, reg_addr);
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		reg_addr = (pbs_mode == PBS_RX_MODE) ?
+			(0x5a + effective_cs * 0x10) :
+			(0x1a + effective_cs * 0x10);
+		CHECK_STATUS(ddr3_tip_bus_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA, reg_addr,
+			      0));
+
+		/* restore cs enable value */
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		CHECK_STATUS(ddr3_tip_if_write
+			     (dev_num, ACCESS_TYPE_UNICAST, if_id,
+			      DUAL_DUNIT_CFG_REG, cs_enable_reg_val[if_id],
+			      MASK_ALL_BITS));
+	}
+
+	/* exit test mode */
+	CHECK_STATUS(ddr3_tip_if_write
+		     (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		      ODPG_WR_RD_MODE_ENA_REG, 0xffff, MASK_ALL_BITS));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup = 0; pup < octets_per_if_num; pup++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+			/*
+			 * no valid window found
+			 * (no lock at EBA ADLL shift at EBS)
+			 */
+			if (pup_state[if_id][pup] == 1)
+				return MV_FAIL;
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_tip_pbs_rx.
+ * Desc:     PBS TX
+ * Args:     TBD
+ * Notes:
+ * Returns:  OK if success, other error code if fail.
+ */
+int ddr3_tip_pbs_rx(u32 uidev_num)
+{
+	return ddr3_tip_pbs(uidev_num, PBS_RX_MODE);
+}
+
+/*
+ * Name:     ddr3_tip_pbs_tx.
+ * Desc:     PBS TX
+ * Args:     TBD
+ * Notes:
+ * Returns:  OK if success, other error code if fail.
+ */
+int ddr3_tip_pbs_tx(u32 uidev_num)
+{
+	return ddr3_tip_pbs(uidev_num, PBS_TX_MODE);
+}
+
+#ifdef DDR_VIEWER_TOOL
+/*
+ * Print PBS Result
+ */
+int ddr3_tip_print_all_pbs_result(u32 dev_num)
+{
+	u32 curr_cs;
+	unsigned int max_cs = mv_ddr_cs_num_get();
+
+	for (curr_cs = 0; curr_cs < max_cs; curr_cs++) {
+		ddr3_tip_print_pbs_result(dev_num, curr_cs, PBS_RX_MODE);
+		ddr3_tip_print_pbs_result(dev_num, curr_cs, PBS_TX_MODE);
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Print PBS Result
+ */
+int ddr3_tip_print_pbs_result(u32 dev_num, u32 cs_num, enum pbs_dir pbs_mode)
+{
+	u32 data_value = 0, bit = 0, if_id = 0, pup = 0;
+	u32 reg_addr = (pbs_mode == PBS_RX_MODE) ?
+		PBS_RX_PHY_REG(cs_num, 0) :
+		PBS_TX_PHY_REG(cs_num , 0);
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	printf("%s,CS%d,PBS,ADLLRATIO,,,",
+	       (pbs_mode == PBS_RX_MODE) ? "Rx" : "Tx", cs_num);
+
+	for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup = 0; pup < octets_per_if_num; pup++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+			printf("%d,",
+			       pbsdelay_per_pup[pbs_mode][if_id][pup][cs_num]);
+		}
+	}
+	printf("CS%d, %s ,PBS\n", cs_num,
+	       (pbs_mode == PBS_RX_MODE) ? "Rx" : "Tx");
+
+	for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) {
+		printf("%s, DQ", (pbs_mode == PBS_RX_MODE) ? "Rx" : "Tx");
+		for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+			VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+			printf("%d ,PBS,,, ", bit);
+			for (pup = 0; pup <= octets_per_if_num;
+			     pup++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, pup);
+				CHECK_STATUS(ddr3_tip_bus_read
+					     (dev_num, if_id,
+					      ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr + bit,
+					      &data_value));
+				printf("%d , ", data_value);
+			}
+		}
+		printf("\n");
+	}
+	printf("\n");
+
+	return MV_OK;
+}
+#endif /* DDR_VIEWER_TOOL */
+
+/*
+ * Fixup PBS Result
+ */
+int ddr3_tip_clean_pbs_result(u32 dev_num, enum pbs_dir pbs_mode)
+{
+	u32 if_id, pup, bit;
+	u32 reg_addr = (pbs_mode == PBS_RX_MODE) ?
+		PBS_RX_PHY_REG(effective_cs, 0) :
+		PBS_TX_PHY_REG(effective_cs, 0);
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+		for (pup = 0; pup <= octets_per_if_num; pup++) {
+			for (bit = 0; bit <= BUS_WIDTH_IN_BITS + 3; bit++) {
+				CHECK_STATUS(ddr3_tip_bus_write
+					     (dev_num, ACCESS_TYPE_UNICAST,
+					      if_id, ACCESS_TYPE_UNICAST, pup,
+					      DDR_PHY_DATA, reg_addr + bit, 0));
+			}
+		}
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/ddr_ml_wrapper.h b/drivers/ddr/marvell/a38x/ddr_ml_wrapper.h
new file mode 100644
index 0000000..5bf3239
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr_ml_wrapper.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR_ML_WRAPPER_H
+#define _DDR_ML_WRAPPER_H
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X)
+#define INTER_REGS_BASE	SOC_REGS_PHY_BASE
+#endif
+
+/*
+ * MV_DEBUG_INIT need to be defines, otherwise the output of the
+ * DDR2 training code is not complete and misleading
+ */
+#define MV_DEBUG_INIT
+
+#ifdef MV_DEBUG_INIT
+#define DEBUG_INIT_S(s)			puts(s)
+#define DEBUG_INIT_D(d, l)		printf("%x", d)
+#define DEBUG_INIT_D_10(d, l)		printf("%d", d)
+#else
+#define DEBUG_INIT_S(s)
+#define DEBUG_INIT_D(d, l)
+#define DEBUG_INIT_D_10(d, l)
+#endif
+
+#ifdef MV_DEBUG_INIT_FULL
+#define DEBUG_INIT_FULL_S(s)		puts(s)
+#define DEBUG_INIT_FULL_D(d, l)		printf("%x", d)
+#define DEBUG_INIT_FULL_D_10(d, l)	printf("%d", d)
+#define DEBUG_WR_REG(reg, val) \
+	{ DEBUG_INIT_S("Write Reg: 0x"); DEBUG_INIT_D((reg), 8); \
+	  DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); }
+#define DEBUG_RD_REG(reg, val) \
+	{ DEBUG_INIT_S("Read  Reg: 0x"); DEBUG_INIT_D((reg), 8); \
+	  DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); }
+#else
+#define DEBUG_INIT_FULL_S(s)
+#define DEBUG_INIT_FULL_D(d, l)
+#define DEBUG_INIT_FULL_D_10(d, l)
+#define DEBUG_WR_REG(reg, val)
+#define DEBUG_RD_REG(reg, val)
+#endif
+
+#define DEBUG_INIT_FULL_C(s, d, l)			\
+	{ DEBUG_INIT_FULL_S(s);				\
+	  DEBUG_INIT_FULL_D(d, l);			\
+	  DEBUG_INIT_FULL_S("\n"); }
+#define DEBUG_INIT_C(s, d, l) \
+	{ DEBUG_INIT_S(s); DEBUG_INIT_D(d, l); DEBUG_INIT_S("\n"); }
+
+/*
+ * Debug (Enable/Disable modules) and Error report
+ */
+
+#ifdef BASIC_DEBUG
+#define MV_DEBUG_WL
+#define MV_DEBUG_RL
+#define MV_DEBUG_DQS_RESULTS
+#endif
+
+#ifdef FULL_DEBUG
+#define MV_DEBUG_WL
+#define MV_DEBUG_RL
+#define MV_DEBUG_DQS
+
+#define MV_DEBUG_PBS
+#define MV_DEBUG_DFS
+#define MV_DEBUG_MAIN_FULL
+#define MV_DEBUG_DFS_FULL
+#define MV_DEBUG_DQS_FULL
+#define MV_DEBUG_RL_FULL
+#define MV_DEBUG_WL_FULL
+#endif
+
+
+/* The following is a list of Marvell status */
+#define MV_ERROR	(-1)
+#define MV_OK		(0x00)	/* Operation succeeded                   */
+#define MV_FAIL		(0x01)	/* Operation failed                      */
+#define MV_BAD_VALUE	(0x02)	/* Illegal value (general)               */
+#define MV_OUT_OF_RANGE	(0x03)	/* The value is out of range             */
+#define MV_BAD_PARAM	(0x04)	/* Illegal parameter in function called  */
+#define MV_BAD_PTR	(0x05)	/* Illegal pointer value                 */
+#define MV_BAD_SIZE	(0x06)	/* Illegal size                          */
+#define MV_BAD_STATE	(0x07)	/* Illegal state of state machine        */
+#define MV_SET_ERROR	(0x08)	/* Set operation failed                  */
+#define MV_GET_ERROR	(0x09)	/* Get operation failed                  */
+#define MV_CREATE_ERROR	(0x0a)	/* Fail while creating an item           */
+#define MV_NOT_FOUND	(0x0b)	/* Item not found                        */
+#define MV_NO_MORE	(0x0c)	/* No more items found                   */
+#define MV_NO_SUCH	(0x0d)	/* No such item                          */
+#define MV_TIMEOUT	(0x0e)	/* Time Out                              */
+#define MV_NO_CHANGE	(0x0f)	/* Parameter(s) is already in this value */
+#define MV_NOT_SUPPORTED (0x10)	/* This request is not support           */
+#define MV_NOT_IMPLEMENTED (0x11) /* Request supported but not implemented*/
+#define MV_NOT_INITIALIZED (0x12) /* The item is not initialized          */
+#define MV_NO_RESOURCE	(0x13)	/* Resource not available (memory ...)   */
+#define MV_FULL		(0x14)	/* Item is full (Queue or table etc...)  */
+#define MV_EMPTY	(0x15)	/* Item is empty (Queue or table etc...) */
+#define MV_INIT_ERROR	(0x16)	/* Error occured while INIT process      */
+#define MV_HW_ERROR	(0x17)	/* Hardware error                        */
+#define MV_TX_ERROR	(0x18)	/* Transmit operation not succeeded      */
+#define MV_RX_ERROR	(0x19)	/* Recieve operation not succeeded       */
+#define MV_NOT_READY	(0x1a)	/* The other side is not ready yet       */
+#define MV_ALREADY_EXIST (0x1b)	/* Tried to create existing item         */
+#define MV_OUT_OF_CPU_MEM   (0x1c) /* Cpu memory allocation failed.      */
+#define MV_NOT_STARTED	(0x1d)	/* Not started yet                       */
+#define MV_BUSY		(0x1e)	/* Item is busy.                         */
+#define MV_TERMINATE	(0x1f)	/* Item terminates it's work.            */
+#define MV_NOT_ALIGNED	(0x20)	/* Wrong alignment                       */
+#define MV_NOT_ALLOWED	(0x21)	/* Operation NOT allowed                 */
+#define MV_WRITE_PROTECT (0x22)	/* Write protected                       */
+#define MV_INVALID	(int)(-1)
+
+/*
+ * Accessor functions for the registers
+ */
+static inline void reg_write(u32 addr, u32 val)
+{
+	writel(val, INTER_REGS_BASE + addr);
+}
+
+static inline u32 reg_read(u32 addr)
+{
+	return readl(INTER_REGS_BASE + addr);
+}
+
+static inline void reg_bit_set(u32 addr, u32 mask)
+{
+	setbits_le32(INTER_REGS_BASE + addr, mask);
+}
+
+static inline void reg_bit_clr(u32 addr, u32 mask)
+{
+	clrbits_le32(INTER_REGS_BASE + addr, mask);
+}
+
+#endif /* _DDR_ML_WRAPPER_H */
diff --git a/drivers/ddr/marvell/a38x/ddr_topology_def.h b/drivers/ddr/marvell/a38x/ddr_topology_def.h
new file mode 100644
index 0000000..e6fe8a0
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr_topology_def.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR_TOPOLOGY_DEF_H
+#define _DDR_TOPOLOGY_DEF_H
+
+#include "ddr3_training_ip_def.h"
+#include "mv_ddr_topology.h"
+#include "mv_ddr_spd.h"
+#include "ddr3_logging_def.h"
+
+#define MV_DDR_MAX_BUS_NUM	9
+#define MV_DDR_MAX_IFACE_NUM	1
+
+struct bus_params {
+	/* Chip Select (CS) bitmask (bits 0-CS0, bit 1- CS1 ...) */
+	u8 cs_bitmask;
+
+	/*
+	 * mirror enable/disable
+	 * (bits 0-CS0 mirroring, bit 1- CS1 mirroring ...)
+	 */
+	int mirror_enable_bitmask;
+
+	/* DQS Swap (polarity) - true if enable */
+	int is_dqs_swap;
+
+	/* CK swap (polarity) - true if enable */
+	int is_ck_swap;
+};
+
+struct if_params {
+	/* bus configuration */
+	struct bus_params as_bus_params[MV_DDR_MAX_BUS_NUM];
+
+	/* Speed Bin Table */
+	enum mv_ddr_speed_bin speed_bin_index;
+
+	/* sdram device width */
+	enum mv_ddr_dev_width bus_width;
+
+	/* total sdram capacity per die, megabits */
+	enum mv_ddr_die_capacity memory_size;
+
+	/* The DDR frequency for each interfaces */
+	enum mv_ddr_freq memory_freq;
+
+	/*
+	 * delay CAS Write Latency
+	 * - 0 for using default value (jedec suggested)
+	 */
+	u8 cas_wl;
+
+	/*
+	 * delay CAS Latency
+	 * - 0 for using default value (jedec suggested)
+	 */
+	u8 cas_l;
+
+	/* operation temperature */
+	enum mv_ddr_temperature interface_temp;
+
+	/* 2T vs 1T mode (by default computed from number of CSs) */
+	enum mv_ddr_timing timing;
+};
+
+/* memory electrical configuration */
+struct mv_ddr_mem_edata {
+	enum mv_ddr_rtt_nom_park_evalue rtt_nom;
+	enum mv_ddr_rtt_nom_park_evalue rtt_park[MAX_CS_NUM];
+	enum mv_ddr_rtt_wr_evalue rtt_wr[MAX_CS_NUM];
+	enum mv_ddr_dic_evalue dic;
+};
+
+/* phy electrical configuration */
+struct mv_ddr_phy_edata {
+	enum mv_ddr_ohm_evalue drv_data_p;
+	enum mv_ddr_ohm_evalue drv_data_n;
+	enum mv_ddr_ohm_evalue drv_ctrl_p;
+	enum mv_ddr_ohm_evalue drv_ctrl_n;
+	enum mv_ddr_ohm_evalue odt_p[MAX_CS_NUM];
+	enum mv_ddr_ohm_evalue odt_n[MAX_CS_NUM];
+};
+
+/* mac electrical configuration */
+struct mv_ddr_mac_edata {
+	enum mv_ddr_odt_cfg_evalue odt_cfg_pat;
+	enum mv_ddr_odt_cfg_evalue odt_cfg_wr;
+	enum mv_ddr_odt_cfg_evalue odt_cfg_rd;
+};
+
+struct mv_ddr_edata {
+	struct mv_ddr_mem_edata mem_edata;
+	struct mv_ddr_phy_edata phy_edata;
+	struct mv_ddr_mac_edata mac_edata;
+};
+
+struct mv_ddr_topology_map {
+	/* debug level configuration */
+	enum mv_ddr_debug_level debug_level;
+
+	/* Number of interfaces (default is 12) */
+	u8 if_act_mask;
+
+	/* Controller configuration per interface */
+	struct if_params interface_params[MV_DDR_MAX_IFACE_NUM];
+
+	/* Bit mask for active buses */
+	u16 bus_act_mask;
+
+	/* source of ddr configuration data */
+	enum mv_ddr_cfg_src cfg_src;
+
+	/* raw spd data */
+	union mv_ddr_spd_data spd_data;
+
+	/* timing parameters */
+	unsigned int timing_data[MV_DDR_TDATA_LAST];
+
+	/* electrical configuration */
+	struct mv_ddr_edata edata;
+
+	/* electrical parameters */
+	unsigned int electrical_data[MV_DDR_EDATA_LAST];
+};
+
+enum mv_ddr_iface_mode {
+	MV_DDR_RAR_ENA,
+	MV_DDR_RAR_DIS,
+};
+
+enum mv_ddr_iface_state {
+	MV_DDR_IFACE_NRDY,	/* not ready */
+	MV_DDR_IFACE_INIT,	/* init'd */
+	MV_DDR_IFACE_RDY,	/* ready */
+	MV_DDR_IFACE_DNE	/* does not exist */
+};
+
+enum mv_ddr_validation {
+	MV_DDR_VAL_DIS,
+	MV_DDR_VAL_RX,
+	MV_DDR_VAL_TX,
+	MV_DDR_VAL_RX_TX
+};
+
+struct mv_ddr_iface {
+	/* base addr of ap ddr interface belongs to */
+	unsigned int ap_base;
+
+	/* ddr interface id */
+	unsigned int id;
+
+	/* ddr interface state */
+	enum mv_ddr_iface_state state;
+
+	/* ddr interface mode (rar enabled/disabled) */
+	enum mv_ddr_iface_mode iface_mode;
+
+	/* ddr interface base address */
+	unsigned long long iface_base_addr;
+
+	/* ddr interface size - ddr flow will update this parameter */
+	unsigned long long iface_byte_size;
+
+	/* ddr i2c spd data address */
+	unsigned int spd_data_addr;
+
+	/* ddr i2c spd page 0 select address */
+	unsigned int spd_page_sel_addr;
+
+	/* ddr interface validation mode */
+	enum mv_ddr_validation validation;
+
+	/* ddr interface topology map */
+	struct mv_ddr_topology_map tm;
+};
+
+struct mv_ddr_iface *mv_ddr_iface_get(void);
+
+/* DDR3 training global configuration parameters */
+struct tune_train_params {
+	u32 ck_delay;
+	u32 phy_reg3_val;
+	u32 g_zpri_data;
+	u32 g_znri_data;
+	u32 g_zpri_ctrl;
+	u32 g_znri_ctrl;
+	u32 g_zpodt_data;
+	u32 g_znodt_data;
+	u32 g_zpodt_ctrl;
+	u32 g_znodt_ctrl;
+	u32 g_dic;
+	u32 g_odt_config;
+	u32 g_rtt_nom;
+	u32 g_rtt_wr;
+	u32 g_rtt_park;
+};
+
+#endif /* _DDR_TOPOLOGY_DEF_H */
diff --git a/drivers/ddr/marvell/a38x/ddr_training_ip_db.h b/drivers/ddr/marvell/a38x/ddr_training_ip_db.h
new file mode 100644
index 0000000..f1b4d8e
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/ddr_training_ip_db.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _DDR_TRAINING_IP_DB_H_
+#define _DDR_TRAINING_IP_DB_H_
+
+#include "ddr3_training_ip_db.h"
+
+u32 pattern_table_get_word(u32 dev_num, enum hws_pattern type, u8 index);
+
+#endif /* _DDR3_TRAINING_IP_DB_H_ */
diff --git a/drivers/ddr/marvell/a38x/dram_if.h b/drivers/ddr/marvell/a38x/dram_if.h
new file mode 100644
index 0000000..4d08464
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/dram_if.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2016 Marvell International Ltd.
+ */
+
+#ifndef _DRAM_IF_H_
+#define _DRAM_IF_H_
+
+/* TODO: update atf to this new prototype */
+int dram_init(void);
+void dram_mmap_config(void);
+unsigned long long dram_iface_mem_sz_get(void);
+#endif /* _DRAM_IF_H_ */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_build_message.c b/drivers/ddr/marvell/a38x/mv_ddr_build_message.c
new file mode 100644
index 0000000..cc6234f
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_build_message.c
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0
+const char mv_ddr_build_message[] = "";
+const char mv_ddr_version_string[] = "mv_ddr: mv_ddr-armada-18.09.2";
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_common.c b/drivers/ddr/marvell/a38x/mv_ddr_common.c
new file mode 100644
index 0000000..7afabbf
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_common.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "mv_ddr_common.h"
+#include "ddr_ml_wrapper.h"
+
+void mv_ddr_ver_print(void)
+{
+	printf("%s %s\n", mv_ddr_version_string, mv_ddr_build_message);
+}
+
+/* ceiling division for positive integers */
+unsigned int ceil_div(unsigned int x, unsigned int y)
+{
+	return (x % y) ? (x / y + 1) : (x / y);
+}
+
+/*
+ * time to number of clocks calculation based on the rounding algorithm
+ * using 97.4% inverse factor per JEDEC Standard No. 21-C, 4.1.2.L-4:
+ * Serial Presence Detect (SPD) for DDR4 SDRAM Modules
+ */
+unsigned int time_to_nclk(unsigned int t, unsigned int tclk)
+{
+	/* t & tclk parameters are in ps */
+	return ((unsigned long)t * 1000 / tclk + 974) / 1000;
+}
+
+/* round division of two positive integers to the nearest whole number */
+int round_div(unsigned int dividend, unsigned int divisor, unsigned int *quotient)
+{
+	if (quotient == NULL) {
+		printf("%s: error: NULL quotient pointer found\n", __func__);
+		return MV_FAIL;
+	}
+
+	if (divisor == 0) {
+		printf("%s: error: division by zero\n", __func__);
+		return MV_FAIL;
+	} else {
+		*quotient = (dividend + divisor / 2) / divisor;
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_common.h b/drivers/ddr/marvell/a38x/mv_ddr_common.h
new file mode 100644
index 0000000..321a390
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_common.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _MV_DDR_COMMON_H
+#define _MV_DDR_COMMON_H
+
+extern const char mv_ddr_build_message[];
+extern const char mv_ddr_version_string[];
+
+#define _1K	0x00000400
+#define _4K	0x00001000
+#define _8K	0x00002000
+#define _16K	0x00004000
+#define _32K	0x00008000
+#define _64K	0x00010000
+#define _128K	0x00020000
+#define _256K	0x00040000
+#define _512K	0x00080000
+
+#define _1M	0x00100000
+#define _2M	0x00200000
+#define _4M	0x00400000
+#define _8M	0x00800000
+#define _16M	0x01000000
+#define _32M	0x02000000
+#define _64M	0x04000000
+#define _128M	0x08000000
+#define _256M	0x10000000
+#define _512M	0x20000000
+
+#define _1G	0x40000000
+#define _2G	0x80000000
+#define _4G	0x100000000
+#define _8G	0x200000000
+#define _16G	0x400000000
+#define _32G	0x800000000
+#define _64G	0x1000000000
+#define _128G	0x2000000000
+
+#define MEGA			1000000
+#define MV_DDR_MEGABYTE		(1024 * 1024)
+#define MV_DDR_32_BITS_MASK	0xffffffff
+
+#define GET_MAX_VALUE(x, y) \
+	(((x) > (y)) ? (x) : (y))
+
+void mv_ddr_ver_print(void);
+unsigned int ceil_div(unsigned int x, unsigned int y);
+unsigned int time_to_nclk(unsigned int t, unsigned int tclk);
+int round_div(unsigned int dividend, unsigned int divisor, unsigned int *quotient);
+
+#endif /* _MV_DDR_COMMON_H */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_plat.c b/drivers/ddr/marvell/a38x/mv_ddr_plat.c
new file mode 100644
index 0000000..cc7942d
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_plat.c
@@ -0,0 +1,1449 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_training_db.h"
+#include "mv_ddr_regs.h"
+#include "mv_ddr_sys_env_lib.h"
+
+#define DDR_INTERFACES_NUM		1
+#define DDR_INTERFACE_OCTETS_NUM	5
+
+/*
+ * 1. L2 filter should be set at binary header to 0xD000000,
+ *    to avoid conflict with internal register IO.
+ * 2. U-Boot modifies internal registers base to 0xf100000,
+ *    and than should update L2 filter accordingly to 0xf000000 (3.75 GB)
+ */
+#define L2_FILTER_FOR_MAX_MEMORY_SIZE	0xC0000000 /* temporary limit l2 filter to 3gb (LSP issue) */
+#define ADDRESS_FILTERING_END_REGISTER	0x8c04
+
+#define DYNAMIC_CS_SIZE_CONFIG
+#define DISABLE_L2_FILTERING_DURING_DDR_TRAINING
+
+/* Termal Sensor Registers */
+#define TSEN_CONTROL_LSB_REG		0xE4070
+#define TSEN_CONTROL_LSB_TC_TRIM_OFFSET	0
+#define TSEN_CONTROL_LSB_TC_TRIM_MASK	(0x7 << TSEN_CONTROL_LSB_TC_TRIM_OFFSET)
+#define TSEN_CONTROL_MSB_REG		0xE4074
+#define TSEN_CONTROL_MSB_RST_OFFSET	8
+#define TSEN_CONTROL_MSB_RST_MASK	(0x1 << TSEN_CONTROL_MSB_RST_OFFSET)
+#define TSEN_STATUS_REG			0xe4078
+#define TSEN_STATUS_READOUT_VALID_OFFSET	10
+#define TSEN_STATUS_READOUT_VALID_MASK	(0x1 <<				\
+					 TSEN_STATUS_READOUT_VALID_OFFSET)
+#define TSEN_STATUS_TEMP_OUT_OFFSET	0
+#define TSEN_STATUS_TEMP_OUT_MASK	(0x3ff << TSEN_STATUS_TEMP_OUT_OFFSET)
+
+static struct dlb_config ddr3_dlb_config_table[] = {
+	{DLB_CTRL_REG, 0x2000005c},
+	{DLB_BUS_OPT_WT_REG, 0x00880000},
+	{DLB_AGING_REG, 0x0f7f007f},
+	{DLB_EVICTION_CTRL_REG, 0x0000129f},
+	{DLB_EVICTION_TIMERS_REG, 0x00ff0000},
+	{DLB_WTS_DIFF_CS_REG, 0x04030802},
+	{DLB_WTS_DIFF_BG_REG, 0x00000a02},
+	{DLB_WTS_SAME_BG_REG, 0x09000a01},
+	{DLB_WTS_CMDS_REG, 0x00020005},
+	{DLB_WTS_ATTR_PRIO_REG, 0x00060f10},
+	{DLB_QUEUE_MAP_REG, 0x00000543},
+	{DLB_SPLIT_REG, 0x00000000},
+	{DLB_USER_CMD_REG, 0x00000000},
+	{0x0, 0x0}
+};
+
+static struct dlb_config *sys_env_dlb_config_ptr_get(void)
+{
+	return &ddr3_dlb_config_table[0];
+}
+
+static u8 a38x_bw_per_freq[MV_DDR_FREQ_LAST] = {
+	0x3,			/* MV_DDR_FREQ_100 */
+	0x4,			/* MV_DDR_FREQ_400 */
+	0x4,			/* MV_DDR_FREQ_533 */
+	0x5,			/* MV_DDR_FREQ_667 */
+	0x5,			/* MV_DDR_FREQ_800 */
+	0x5,			/* MV_DDR_FREQ_933 */
+	0x5,			/* MV_DDR_FREQ_1066 */
+	0x3,			/* MV_DDR_FREQ_311 */
+	0x3,			/* MV_DDR_FREQ_333 */
+	0x4,			/* MV_DDR_FREQ_467 */
+	0x5,			/* MV_DDR_FREQ_850 */
+	0x5,			/* MV_DDR_FREQ_600 */
+	0x3,			/* MV_DDR_FREQ_300 */
+	0x5,			/* MV_DDR_FREQ_900 */
+	0x3,			/* MV_DDR_FREQ_360 */
+	0x5			/* MV_DDR_FREQ_1000 */
+};
+
+static u8 a38x_rate_per_freq[MV_DDR_FREQ_LAST] = {
+	0x1,			/* MV_DDR_FREQ_100 */
+	0x2,			/* MV_DDR_FREQ_400 */
+	0x2,			/* MV_DDR_FREQ_533 */
+	0x2,			/* MV_DDR_FREQ_667 */
+	0x2,			/* MV_DDR_FREQ_800 */
+	0x3,			/* MV_DDR_FREQ_933 */
+	0x3,			/* MV_DDR_FREQ_1066 */
+	0x1,			/* MV_DDR_FREQ_311 */
+	0x1,			/* MV_DDR_FREQ_333 */
+	0x2,			/* MV_DDR_FREQ_467 */
+	0x2,			/* MV_DDR_FREQ_850 */
+	0x2,			/* MV_DDR_FREQ_600 */
+	0x1,			/* MV_DDR_FREQ_300 */
+	0x2,			/* MV_DDR_FREQ_900 */
+	0x1,			/* MV_DDR_FREQ_360 */
+	0x2			/* MV_DDR_FREQ_1000 */
+};
+
+static u16 a38x_vco_freq_per_sar_ref_clk_25_mhz[] = {
+	666,			/* 0 */
+	1332,
+	800,
+	1600,
+	1066,
+	2132,
+	1200,
+	2400,
+	1332,
+	1332,
+	1500,
+	1500,
+	1600,			/* 12 */
+	1600,
+	1700,
+	1700,
+	1866,
+	1866,
+	1800,			/* 18 */
+	2000,
+	2000,
+	4000,
+	2132,
+	2132,
+	2300,
+	2300,
+	2400,
+	2400,
+	2500,
+	2500,
+	800
+};
+
+static u16 a38x_vco_freq_per_sar_ref_clk_40_mhz[] = {
+	666,			/* 0 */
+	1332,
+	800,
+	800,			/* 0x3 */
+	1066,
+	1066,			/* 0x5 */
+	1200,
+	2400,
+	1332,
+	1332,
+	1500,			/* 10 */
+	1600,			/* 0xB */
+	1600,
+	1600,
+	1700,
+	1560,			/* 0xF */
+	1866,
+	1866,
+	1800,
+	2000,
+	2000,			/* 20 */
+	4000,
+	2132,
+	2132,
+	2300,
+	2300,
+	2400,
+	2400,
+	2500,
+	2500,
+	1800			/* 30 - 0x1E */
+};
+
+
+static u32 async_mode_at_tf;
+
+static u32 dq_bit_map_2_phy_pin[] = {
+	1, 0, 2, 6, 9, 8, 3, 7,	/* 0 */
+	8, 9, 1, 7, 2, 6, 3, 0,	/* 1 */
+	3, 9, 7, 8, 1, 0, 2, 6,	/* 2 */
+	1, 0, 6, 2, 8, 3, 7, 9,	/* 3 */
+	0, 1, 2, 9, 7, 8, 3, 6,	/* 4 */
+};
+
+void mv_ddr_mem_scrubbing(void)
+{
+	ddr3_new_tip_ecc_scrub();
+}
+
+static int ddr3_tip_a38x_set_divider(u8 dev_num, u32 if_id,
+				     enum mv_ddr_freq freq);
+
+/*
+ * Read temperature TJ value
+ */
+static u32 ddr3_ctrl_get_junc_temp(u8 dev_num)
+{
+	int reg = 0;
+
+	/* Initiates TSEN hardware reset once */
+	if ((reg_read(TSEN_CONTROL_MSB_REG) & TSEN_CONTROL_MSB_RST_MASK) == 0) {
+		reg_bit_set(TSEN_CONTROL_MSB_REG, TSEN_CONTROL_MSB_RST_MASK);
+		/* set Tsen Tc Trim to correct default value (errata #132698) */
+		reg = reg_read(TSEN_CONTROL_LSB_REG);
+		reg &= ~TSEN_CONTROL_LSB_TC_TRIM_MASK;
+		reg |= 0x3 << TSEN_CONTROL_LSB_TC_TRIM_OFFSET;
+		reg_write(TSEN_CONTROL_LSB_REG, reg);
+	}
+	mdelay(10);
+
+	/* Check if the readout field is valid */
+	if ((reg_read(TSEN_STATUS_REG) & TSEN_STATUS_READOUT_VALID_MASK) == 0) {
+		printf("%s: TSEN not ready\n", __func__);
+		return 0;
+	}
+
+	reg = reg_read(TSEN_STATUS_REG);
+	reg = (reg & TSEN_STATUS_TEMP_OUT_MASK) >> TSEN_STATUS_TEMP_OUT_OFFSET;
+
+	return ((((10000 * reg) / 21445) * 1000) - 272674) / 1000;
+}
+
+/*
+ * Name:     ddr3_tip_a38x_get_freq_config.
+ * Desc:
+ * Args:
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static int ddr3_tip_a38x_get_freq_config(u8 dev_num, enum mv_ddr_freq freq,
+				  struct hws_tip_freq_config_info
+				  *freq_config_info)
+{
+	if (a38x_bw_per_freq[freq] == 0xff)
+		return MV_NOT_SUPPORTED;
+
+	if (freq_config_info == NULL)
+		return MV_BAD_PARAM;
+
+	freq_config_info->bw_per_freq = a38x_bw_per_freq[freq];
+	freq_config_info->rate_per_freq = a38x_rate_per_freq[freq];
+	freq_config_info->is_supported = 1;
+
+	return MV_OK;
+}
+
+static void dunit_read(u32 addr, u32 mask, u32 *data)
+{
+	*data = reg_read(addr) & mask;
+}
+
+static void dunit_write(u32 addr, u32 mask, u32 data)
+{
+	u32 reg_val = data;
+
+	if (mask != MASK_ALL_BITS) {
+		dunit_read(addr, MASK_ALL_BITS, &reg_val);
+		reg_val &= (~mask);
+		reg_val |= (data & mask);
+	}
+
+	reg_write(addr, reg_val);
+}
+
+#define ODPG_ENABLE_REG				0x186d4
+#define ODPG_EN_OFFS				0
+#define ODPG_EN_MASK				0x1
+#define ODPG_EN_ENA				1
+#define ODPG_EN_DONE				0
+#define ODPG_DIS_OFFS				8
+#define ODPG_DIS_MASK				0x1
+#define ODPG_DIS_DIS				1
+void mv_ddr_odpg_enable(void)
+{
+	dunit_write(ODPG_ENABLE_REG,
+		    ODPG_EN_MASK << ODPG_EN_OFFS,
+		    ODPG_EN_ENA << ODPG_EN_OFFS);
+}
+
+void mv_ddr_odpg_disable(void)
+{
+	dunit_write(ODPG_ENABLE_REG,
+		    ODPG_DIS_MASK << ODPG_DIS_OFFS,
+		    ODPG_DIS_DIS << ODPG_DIS_OFFS);
+}
+
+void mv_ddr_odpg_done_clr(void)
+{
+	return;
+}
+
+int mv_ddr_is_odpg_done(u32 count)
+{
+	u32 i, data;
+
+	for (i = 0; i < count; i++) {
+		dunit_read(ODPG_ENABLE_REG, MASK_ALL_BITS, &data);
+		if (((data >> ODPG_EN_OFFS) & ODPG_EN_MASK) ==
+		     ODPG_EN_DONE)
+			break;
+	}
+
+	if (i >= count) {
+		printf("%s: timeout\n", __func__);
+		return MV_FAIL;
+	}
+
+	return MV_OK;
+}
+
+void mv_ddr_training_enable(void)
+{
+	dunit_write(GLOB_CTRL_STATUS_REG,
+		    TRAINING_TRIGGER_MASK << TRAINING_TRIGGER_OFFS,
+		    TRAINING_TRIGGER_ENA << TRAINING_TRIGGER_OFFS);
+}
+
+#define DRAM_INIT_CTRL_STATUS_REG	0x18488
+#define TRAINING_TRIGGER_OFFS		0
+#define TRAINING_TRIGGER_MASK		0x1
+#define TRAINING_TRIGGER_ENA		1
+#define TRAINING_DONE_OFFS		1
+#define TRAINING_DONE_MASK		0x1
+#define TRAINING_DONE_DONE		1
+#define TRAINING_DONE_NOT_DONE		0
+#define TRAINING_RESULT_OFFS		2
+#define TRAINING_RESULT_MASK		0x1
+#define TRAINING_RESULT_PASS		0
+#define TRAINING_RESULT_FAIL		1
+int mv_ddr_is_training_done(u32 count, u32 *result)
+{
+	u32 i, data;
+
+	if (result == NULL) {
+		printf("%s: NULL result pointer found\n", __func__);
+		return MV_FAIL;
+	}
+
+	for (i = 0; i < count; i++) {
+		dunit_read(DRAM_INIT_CTRL_STATUS_REG, MASK_ALL_BITS, &data);
+		if (((data >> TRAINING_DONE_OFFS) & TRAINING_DONE_MASK) ==
+		     TRAINING_DONE_DONE)
+			break;
+	}
+
+	if (i >= count) {
+		printf("%s: timeout\n", __func__);
+		return MV_FAIL;
+	}
+
+	*result = (data >> TRAINING_RESULT_OFFS) & TRAINING_RESULT_MASK;
+
+	return MV_OK;
+}
+
+#define DM_PAD	10
+u32 mv_ddr_dm_pad_get(void)
+{
+	return DM_PAD;
+}
+
+/*
+ * Name:     ddr3_tip_a38x_select_ddr_controller.
+ * Desc:     Enable/Disable access to Marvell's server.
+ * Args:     dev_num     - device number
+ *           enable        - whether to enable or disable the server
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static int ddr3_tip_a38x_select_ddr_controller(u8 dev_num, int enable)
+{
+	u32 reg;
+
+	reg = reg_read(DUAL_DUNIT_CFG_REG);
+
+	if (enable)
+		reg |= (1 << 6);
+	else
+		reg &= ~(1 << 6);
+
+	reg_write(DUAL_DUNIT_CFG_REG, reg);
+
+	return MV_OK;
+}
+
+static u8 ddr3_tip_clock_mode(u32 frequency)
+{
+	if ((frequency == MV_DDR_FREQ_LOW_FREQ) || (mv_ddr_freq_get(frequency) <= 400))
+		return 1;
+
+	return 2;
+}
+
+static int mv_ddr_sar_freq_get(int dev_num, enum mv_ddr_freq *freq)
+{
+	u32 reg, ref_clk_satr;
+
+	/* Read sample at reset setting */
+	reg = (reg_read(REG_DEVICE_SAR1_ADDR) >>
+	       RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET) &
+		RST2_CPU_DDR_CLOCK_SELECT_IN_MASK;
+
+	ref_clk_satr = reg_read(DEVICE_SAMPLE_AT_RESET2_REG);
+	if (((ref_clk_satr >> DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_OFFSET) & 0x1) ==
+	    DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_25MHZ) {
+		switch (reg) {
+		case 0x1:
+			DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR,
+					      ("Warning: Unsupported freq mode for 333Mhz configured(%d)\n",
+					      reg));
+			/* fallthrough */
+		case 0x0:
+			*freq = MV_DDR_FREQ_333;
+			break;
+		case 0x3:
+			DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR,
+					      ("Warning: Unsupported freq mode for 400Mhz configured(%d)\n",
+					      reg));
+			/* fallthrough */
+		case 0x2:
+			*freq = MV_DDR_FREQ_400;
+			break;
+		case 0xd:
+			DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR,
+					      ("Warning: Unsupported freq mode for 533Mhz configured(%d)\n",
+					      reg));
+			/* fallthrough */
+		case 0x4:
+			*freq = MV_DDR_FREQ_533;
+			break;
+		case 0x6:
+			*freq = MV_DDR_FREQ_600;
+			break;
+		case 0x11:
+		case 0x14:
+			DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR,
+					      ("Warning: Unsupported freq mode for 667Mhz configured(%d)\n",
+					      reg));
+			/* fallthrough */
+		case 0x8:
+			*freq = MV_DDR_FREQ_667;
+			break;
+		case 0x15:
+		case 0x1b:
+			DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR,
+					      ("Warning: Unsupported freq mode for 800Mhz configured(%d)\n",
+					      reg));
+			/* fallthrough */
+		case 0xc:
+			*freq = MV_DDR_FREQ_800;
+			break;
+		case 0x10:
+			*freq = MV_DDR_FREQ_933;
+			break;
+		case 0x12:
+			*freq = MV_DDR_FREQ_900;
+			break;
+		case 0x13:
+			*freq = MV_DDR_FREQ_933;
+			break;
+		default:
+			*freq = 0;
+			return MV_NOT_SUPPORTED;
+		}
+	} else { /* REFCLK 40MHz case */
+		switch (reg) {
+		case 0x3:
+			*freq = MV_DDR_FREQ_400;
+			break;
+		case 0x5:
+			*freq = MV_DDR_FREQ_533;
+			break;
+		case 0xb:
+			*freq = MV_DDR_FREQ_800;
+			break;
+		case 0x1e:
+			*freq = MV_DDR_FREQ_900;
+			break;
+		default:
+			*freq = 0;
+			return MV_NOT_SUPPORTED;
+		}
+	}
+
+	return MV_OK;
+}
+
+static int ddr3_tip_a38x_get_medium_freq(int dev_num, enum mv_ddr_freq *freq)
+{
+	u32 reg, ref_clk_satr;
+
+	/* Read sample at reset setting */
+	reg = (reg_read(REG_DEVICE_SAR1_ADDR) >>
+	RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET) &
+	RST2_CPU_DDR_CLOCK_SELECT_IN_MASK;
+
+	ref_clk_satr = reg_read(DEVICE_SAMPLE_AT_RESET2_REG);
+	if (((ref_clk_satr >> DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_OFFSET) & 0x1) ==
+	    DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_25MHZ) {
+		switch (reg) {
+		case 0x0:
+		case 0x1:
+			/* Medium is same as TF to run PBS in this freq */
+			*freq = MV_DDR_FREQ_333;
+			break;
+		case 0x2:
+		case 0x3:
+			/* Medium is same as TF to run PBS in this freq */
+			*freq = MV_DDR_FREQ_400;
+			break;
+		case 0x4:
+		case 0xd:
+			/* Medium is same as TF to run PBS in this freq */
+			*freq = MV_DDR_FREQ_533;
+			break;
+		case 0x8:
+		case 0x10:
+		case 0x11:
+		case 0x14:
+			*freq = MV_DDR_FREQ_333;
+			break;
+		case 0xc:
+		case 0x15:
+		case 0x1b:
+			*freq = MV_DDR_FREQ_400;
+			break;
+		case 0x6:
+			*freq = MV_DDR_FREQ_300;
+			break;
+		case 0x12:
+			*freq = MV_DDR_FREQ_360;
+			break;
+		case 0x13:
+			*freq = MV_DDR_FREQ_400;
+			break;
+		default:
+			*freq = 0;
+			return MV_NOT_SUPPORTED;
+		}
+	} else { /* REFCLK 40MHz case */
+		switch (reg) {
+		case 0x3:
+			/* Medium is same as TF to run PBS in this freq */
+			*freq = MV_DDR_FREQ_400;
+			break;
+		case 0x5:
+			/* Medium is same as TF to run PBS in this freq */
+			*freq = MV_DDR_FREQ_533;
+			break;
+		case 0xb:
+			*freq = MV_DDR_FREQ_400;
+			break;
+		case 0x1e:
+			*freq = MV_DDR_FREQ_360;
+			break;
+		default:
+			*freq = 0;
+			return MV_NOT_SUPPORTED;
+		}
+	}
+
+	return MV_OK;
+}
+
+static int ddr3_tip_a38x_get_device_info(u8 dev_num, struct ddr3_device_info *info_ptr)
+{
+#if defined(CONFIG_ARMADA_39X)
+	info_ptr->device_id = 0x6900;
+#else
+	info_ptr->device_id = 0x6800;
+#endif
+	info_ptr->ck_delay = ck_delay;
+
+	return MV_OK;
+}
+
+/* check indirect access to phy register file completed */
+static int is_prfa_done(void)
+{
+	u32 reg_val;
+	u32 iter = 0;
+
+	do {
+		if (iter++ > MAX_POLLING_ITERATIONS) {
+			printf("error: %s: polling timeout\n", __func__);
+			return MV_FAIL;
+		}
+		dunit_read(PHY_REG_FILE_ACCESS_REG, MASK_ALL_BITS, &reg_val);
+		reg_val >>= PRFA_REQ_OFFS;
+		reg_val &= PRFA_REQ_MASK;
+	} while (reg_val == PRFA_REQ_ENA); /* request pending */
+
+	return MV_OK;
+}
+
+/* write to phy register thru indirect access */
+static int prfa_write(enum hws_access_type phy_access, u32 phy,
+		      enum hws_ddr_phy phy_type, u32 addr,
+		      u32 data, enum hws_operation op_type)
+{
+	u32 reg_val = ((data & PRFA_DATA_MASK) << PRFA_DATA_OFFS) |
+		      ((addr & PRFA_REG_NUM_MASK) << PRFA_REG_NUM_OFFS) |
+		      ((phy & PRFA_PUP_NUM_MASK) << PRFA_PUP_NUM_OFFS) |
+		      ((phy_type & PRFA_PUP_CTRL_DATA_MASK) << PRFA_PUP_CTRL_DATA_OFFS) |
+		      ((phy_access & PRFA_PUP_BCAST_WR_ENA_MASK) << PRFA_PUP_BCAST_WR_ENA_OFFS) |
+		      (((addr >> 6) & PRFA_REG_NUM_HI_MASK) << PRFA_REG_NUM_HI_OFFS) |
+		      ((op_type & PRFA_TYPE_MASK) << PRFA_TYPE_OFFS);
+	dunit_write(PHY_REG_FILE_ACCESS_REG, MASK_ALL_BITS, reg_val);
+	reg_val |= (PRFA_REQ_ENA << PRFA_REQ_OFFS);
+	dunit_write(PHY_REG_FILE_ACCESS_REG, MASK_ALL_BITS, reg_val);
+
+	/* polling for prfa request completion */
+	if (is_prfa_done() != MV_OK)
+		return MV_FAIL;
+
+	return MV_OK;
+}
+
+/* read from phy register thru indirect access */
+static int prfa_read(enum hws_access_type phy_access, u32 phy,
+		     enum hws_ddr_phy phy_type, u32 addr, u32 *data)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	u32 max_phy = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+	u32 i, reg_val;
+
+	if (phy_access == ACCESS_TYPE_MULTICAST) {
+		for (i = 0; i < max_phy; i++) {
+			VALIDATE_BUS_ACTIVE(tm->bus_act_mask, i);
+			if (prfa_write(ACCESS_TYPE_UNICAST, i, phy_type, addr, 0, OPERATION_READ) != MV_OK)
+				return MV_FAIL;
+			dunit_read(PHY_REG_FILE_ACCESS_REG, MASK_ALL_BITS, &reg_val);
+			data[i] = (reg_val >> PRFA_DATA_OFFS) & PRFA_DATA_MASK;
+		}
+	} else {
+		if (prfa_write(phy_access, phy, phy_type, addr, 0, OPERATION_READ) != MV_OK)
+			return MV_FAIL;
+		dunit_read(PHY_REG_FILE_ACCESS_REG, MASK_ALL_BITS, &reg_val);
+		*data = (reg_val >> PRFA_DATA_OFFS) & PRFA_DATA_MASK;
+	}
+
+	return MV_OK;
+}
+
+static int mv_ddr_sw_db_init(u32 dev_num, u32 board_id)
+{
+	struct hws_tip_config_func_db config_func;
+
+	/* new read leveling version */
+	config_func.mv_ddr_dunit_read = dunit_read;
+	config_func.mv_ddr_dunit_write = dunit_write;
+	config_func.tip_dunit_mux_select_func =
+		ddr3_tip_a38x_select_ddr_controller;
+	config_func.tip_get_freq_config_info_func =
+		ddr3_tip_a38x_get_freq_config;
+	config_func.tip_set_freq_divider_func = ddr3_tip_a38x_set_divider;
+	config_func.tip_get_device_info_func = ddr3_tip_a38x_get_device_info;
+	config_func.tip_get_temperature = ddr3_ctrl_get_junc_temp;
+	config_func.tip_get_clock_ratio = ddr3_tip_clock_mode;
+	config_func.tip_external_read = ddr3_tip_ext_read;
+	config_func.tip_external_write = ddr3_tip_ext_write;
+	config_func.mv_ddr_phy_read = prfa_read;
+	config_func.mv_ddr_phy_write = prfa_write;
+
+	ddr3_tip_init_config_func(dev_num, &config_func);
+
+	ddr3_tip_register_dq_table(dev_num, dq_bit_map_2_phy_pin);
+
+	/* set device attributes*/
+	ddr3_tip_dev_attr_init(dev_num);
+	ddr3_tip_dev_attr_set(dev_num, MV_ATTR_TIP_REV, MV_TIP_REV_4);
+	ddr3_tip_dev_attr_set(dev_num, MV_ATTR_PHY_EDGE, MV_DDR_PHY_EDGE_POSITIVE);
+	ddr3_tip_dev_attr_set(dev_num, MV_ATTR_OCTET_PER_INTERFACE, DDR_INTERFACE_OCTETS_NUM);
+#ifdef CONFIG_ARMADA_39X
+	ddr3_tip_dev_attr_set(dev_num, MV_ATTR_INTERLEAVE_WA, 1);
+#else
+	ddr3_tip_dev_attr_set(dev_num, MV_ATTR_INTERLEAVE_WA, 0);
+#endif
+
+	ca_delay = 0;
+	delay_enable = 1;
+	dfs_low_freq = DFS_LOW_FREQ_VALUE;
+	calibration_update_control = 1;
+
+	ddr3_tip_a38x_get_medium_freq(dev_num, &medium_freq);
+
+	return MV_OK;
+}
+
+static int mv_ddr_training_mask_set(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	enum mv_ddr_freq ddr_freq = tm->interface_params[0].memory_freq;
+
+	mask_tune_func = (SET_LOW_FREQ_MASK_BIT |
+			  LOAD_PATTERN_MASK_BIT |
+			  SET_MEDIUM_FREQ_MASK_BIT | WRITE_LEVELING_MASK_BIT |
+			  WRITE_LEVELING_SUPP_MASK_BIT |
+			  READ_LEVELING_MASK_BIT |
+			  PBS_RX_MASK_BIT |
+			  PBS_TX_MASK_BIT |
+			  SET_TARGET_FREQ_MASK_BIT |
+			  WRITE_LEVELING_TF_MASK_BIT |
+			  WRITE_LEVELING_SUPP_TF_MASK_BIT |
+			  READ_LEVELING_TF_MASK_BIT |
+			  CENTRALIZATION_RX_MASK_BIT |
+			  CENTRALIZATION_TX_MASK_BIT);
+	rl_mid_freq_wa = 1;
+
+	if ((ddr_freq == MV_DDR_FREQ_333) || (ddr_freq == MV_DDR_FREQ_400)) {
+		mask_tune_func = (WRITE_LEVELING_MASK_BIT |
+				  LOAD_PATTERN_2_MASK_BIT |
+				  WRITE_LEVELING_SUPP_MASK_BIT |
+				  READ_LEVELING_MASK_BIT |
+				  PBS_RX_MASK_BIT |
+				  PBS_TX_MASK_BIT |
+				  CENTRALIZATION_RX_MASK_BIT |
+				  CENTRALIZATION_TX_MASK_BIT);
+		rl_mid_freq_wa = 0; /* WA not needed if 333/400 is TF */
+	}
+
+	/* Supplementary not supported for ECC modes */
+	if (mv_ddr_is_ecc_ena()) {
+		mask_tune_func &= ~WRITE_LEVELING_SUPP_TF_MASK_BIT;
+		mask_tune_func &= ~WRITE_LEVELING_SUPP_MASK_BIT;
+		mask_tune_func &= ~PBS_TX_MASK_BIT;
+		mask_tune_func &= ~PBS_RX_MASK_BIT;
+	}
+
+	return MV_OK;
+}
+
+/* function: mv_ddr_set_calib_controller
+ * this function sets the controller which will control
+ * the calibration cycle in the end of the training.
+ * 1 - internal controller
+ * 2 - external controller
+ */
+void mv_ddr_set_calib_controller(void)
+{
+	calibration_update_control = CAL_UPDATE_CTRL_INT;
+}
+
+static int ddr3_tip_a38x_set_divider(u8 dev_num, u32 if_id,
+				     enum mv_ddr_freq frequency)
+{
+	u32 divider = 0;
+	u32 sar_val, ref_clk_satr;
+	u32 async_val;
+	u32 freq = mv_ddr_freq_get(frequency);
+
+	if (if_id != 0) {
+		DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR,
+				      ("A38x does not support interface 0x%x\n",
+				       if_id));
+		return MV_BAD_PARAM;
+	}
+
+	/* get VCO freq index */
+	sar_val = (reg_read(REG_DEVICE_SAR1_ADDR) >>
+		   RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET) &
+		RST2_CPU_DDR_CLOCK_SELECT_IN_MASK;
+
+	ref_clk_satr = reg_read(DEVICE_SAMPLE_AT_RESET2_REG);
+	if (((ref_clk_satr >> DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_OFFSET) & 0x1) ==
+	    DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_25MHZ)
+		divider = a38x_vco_freq_per_sar_ref_clk_25_mhz[sar_val] / freq;
+	else
+		divider = a38x_vco_freq_per_sar_ref_clk_40_mhz[sar_val] / freq;
+
+	if ((async_mode_at_tf == 1) && (freq > 400)) {
+		/* Set async mode */
+		dunit_write(0x20220, 0x1000, 0x1000);
+		dunit_write(0xe42f4, 0x200, 0x200);
+
+		/* Wait for async mode setup */
+		mdelay(5);
+
+		/* Set KNL values */
+		switch (frequency) {
+		case MV_DDR_FREQ_467:
+			async_val = 0x806f012;
+			break;
+		case MV_DDR_FREQ_533:
+			async_val = 0x807f012;
+			break;
+		case MV_DDR_FREQ_600:
+			async_val = 0x805f00a;
+			break;
+		case MV_DDR_FREQ_667:
+			async_val = 0x809f012;
+			break;
+		case MV_DDR_FREQ_800:
+			async_val = 0x807f00a;
+			break;
+		case MV_DDR_FREQ_850:
+			async_val = 0x80cb012;
+			break;
+		case MV_DDR_FREQ_900:
+			async_val = 0x80d7012;
+			break;
+		case MV_DDR_FREQ_933:
+			async_val = 0x80df012;
+			break;
+		case MV_DDR_FREQ_1000:
+			async_val = 0x80ef012;
+			break;
+		case MV_DDR_FREQ_1066:
+			async_val = 0x80ff012;
+			break;
+		default:
+			/* set MV_DDR_FREQ_667 as default */
+			async_val = 0x809f012;
+		}
+		dunit_write(0xe42f0, 0xffffffff, async_val);
+	} else {
+		/* Set sync mode */
+		dunit_write(0x20220, 0x1000, 0x0);
+		dunit_write(0xe42f4, 0x200, 0x0);
+
+		/* cpupll_clkdiv_reset_mask */
+		dunit_write(0xe4264, 0xff, 0x1f);
+
+		/* cpupll_clkdiv_reload_smooth */
+		dunit_write(0xe4260, (0xff << 8), (0x2 << 8));
+
+		/* cpupll_clkdiv_relax_en */
+		dunit_write(0xe4260, (0xff << 24), (0x2 << 24));
+
+		/* write the divider */
+		dunit_write(0xe4268, (0x3f << 8), (divider << 8));
+
+		/* set cpupll_clkdiv_reload_ratio */
+		dunit_write(0xe4264, (1 << 8), (1 << 8));
+
+		/* undet cpupll_clkdiv_reload_ratio */
+		dunit_write(0xe4264, (1 << 8), 0x0);
+
+		/* clear cpupll_clkdiv_reload_force */
+		dunit_write(0xe4260, (0xff << 8), 0x0);
+
+		/* clear cpupll_clkdiv_relax_en */
+		dunit_write(0xe4260, (0xff << 24), 0x0);
+
+		/* clear cpupll_clkdiv_reset_mask */
+		dunit_write(0xe4264, 0xff, 0x0);
+	}
+
+	/* Dunit training clock + 1:1/2:1 mode */
+	dunit_write(0x18488, (1 << 16), ((ddr3_tip_clock_mode(frequency) & 0x1) << 16));
+	dunit_write(0x1524, (1 << 15), ((ddr3_tip_clock_mode(frequency) - 1) << 15));
+
+	return MV_OK;
+}
+
+/*
+ * external read from memory
+ */
+int ddr3_tip_ext_read(u32 dev_num, u32 if_id, u32 reg_addr,
+		      u32 num_of_bursts, u32 *data)
+{
+	u32 burst_num;
+
+	for (burst_num = 0; burst_num < num_of_bursts * 8; burst_num++)
+		data[burst_num] = readl(reg_addr + 4 * burst_num);
+
+	return MV_OK;
+}
+
+/*
+ * external write to memory
+ */
+int ddr3_tip_ext_write(u32 dev_num, u32 if_id, u32 reg_addr,
+		       u32 num_of_bursts, u32 *data) {
+	u32 burst_num;
+
+	for (burst_num = 0; burst_num < num_of_bursts * 8; burst_num++)
+		writel(data[burst_num], reg_addr + 4 * burst_num);
+
+	return MV_OK;
+}
+
+int mv_ddr_early_init(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* FIXME: change this configuration per ddr type
+	 * configure a380 and a390 to work with receiver odt timing
+	 * the odt_config is defined:
+	 * '1' in ddr4
+	 * '0' in ddr3
+	 * here the parameter is run over in ddr4 and ddr3 to '1' (in ddr4 the default is '1')
+	 * to configure the odt to work with timing restrictions
+	 */
+
+	mv_ddr_sw_db_init(0, 0);
+
+	if (tm->interface_params[0].memory_freq != MV_DDR_FREQ_SAR)
+		async_mode_at_tf = 1;
+
+	return MV_OK;
+}
+
+int mv_ddr_early_init2(void)
+{
+	mv_ddr_training_mask_set();
+
+	return MV_OK;
+}
+
+int mv_ddr_pre_training_fixup(void)
+{
+	return 0;
+}
+
+int mv_ddr_post_training_fixup(void)
+{
+	return 0;
+}
+
+int ddr3_post_run_alg(void)
+{
+	return MV_OK;
+}
+
+int ddr3_silicon_post_init(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	/* Set half bus width */
+	if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask)) {
+		CHECK_STATUS(ddr3_tip_if_write
+			     (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
+			      SDRAM_CFG_REG, 0x0, 0x8000));
+	}
+
+	return MV_OK;
+}
+
+u32 mv_ddr_init_freq_get(void)
+{
+	enum mv_ddr_freq freq;
+
+	mv_ddr_sar_freq_get(0, &freq);
+
+	return freq;
+}
+
+static u32 ddr3_get_bus_width(void)
+{
+	u32 bus_width;
+
+	bus_width = (reg_read(SDRAM_CFG_REG) & 0x8000) >>
+		BUS_IN_USE_OFFS;
+
+	return (bus_width == 0) ? 16 : 32;
+}
+
+static u32 ddr3_get_device_width(u32 cs)
+{
+	u32 device_width;
+
+	device_width = (reg_read(SDRAM_ADDR_CTRL_REG) &
+			(CS_STRUCT_MASK << CS_STRUCT_OFFS(cs))) >>
+			CS_STRUCT_OFFS(cs);
+
+	return (device_width == 0) ? 8 : 16;
+}
+
+static u32 ddr3_get_device_size(u32 cs)
+{
+	u32 device_size_low, device_size_high, device_size;
+	u32 data, cs_low_offset, cs_high_offset;
+
+	cs_low_offset = CS_SIZE_OFFS(cs);
+	cs_high_offset = CS_SIZE_HIGH_OFFS(cs);
+
+	data = reg_read(SDRAM_ADDR_CTRL_REG);
+	device_size_low = (data >> cs_low_offset) & 0x3;
+	device_size_high = (data >> cs_high_offset) & 0x1;
+
+	device_size = device_size_low | (device_size_high << 2);
+
+	switch (device_size) {
+	case 0:
+		return 2048;
+	case 2:
+		return 512;
+	case 3:
+		return 1024;
+	case 4:
+		return 4096;
+	case 5:
+		return 8192;
+	case 1:
+	default:
+		DEBUG_INIT_C("Error: Wrong device size of Cs: ", cs, 1);
+		/* zeroes mem size in ddr3_calc_mem_cs_size */
+		return 0;
+	}
+}
+
+int ddr3_calc_mem_cs_size(u32 cs, uint64_t *cs_size)
+{
+	u32 cs_mem_size;
+
+	/* Calculate in MiB */
+	cs_mem_size = ((ddr3_get_bus_width() / ddr3_get_device_width(cs)) *
+		       ddr3_get_device_size(cs)) / 8;
+
+	/*
+	 * Multiple controller bus width, 2x for 64 bit
+	 * (SoC controller may be 32 or 64 bit,
+	 * so bit 15 in 0x1400, that means if whole bus used or only half,
+	 * have a differnt meaning
+	 */
+	cs_mem_size *= DDR_CONTROLLER_BUS_WIDTH_MULTIPLIER;
+
+	if ((cs_mem_size < 128) || (cs_mem_size > 4096)) {
+		DEBUG_INIT_C("Error: Wrong Memory size of Cs: ", cs, 1);
+		return MV_BAD_VALUE;
+	}
+
+	*cs_size = cs_mem_size << 20; /* write cs size in bytes */
+
+	return MV_OK;
+}
+
+static int ddr3_fast_path_dynamic_cs_size_config(u32 cs_ena)
+{
+	u32 reg, cs;
+	uint64_t mem_total_size = 0;
+	uint64_t cs_mem_size = 0;
+	uint64_t mem_total_size_c, cs_mem_size_c;
+
+#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE
+	u32 physical_mem_size;
+	u32 max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+#endif
+
+	/* Open fast path windows */
+	for (cs = 0; cs < MAX_CS_NUM; cs++) {
+		if (cs_ena & (1 << cs)) {
+			/* get CS size */
+			if (ddr3_calc_mem_cs_size(cs, &cs_mem_size) != MV_OK)
+				return MV_FAIL;
+
+#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE
+			/*
+			 * if number of address pins doesn't allow to use max
+			 * mem size that is defined in topology
+			 * mem size is defined by DEVICE_MAX_DRAM_ADDRESS_SIZE
+			 */
+			physical_mem_size = mem_size
+				[tm->interface_params[0].memory_size];
+
+			if (ddr3_get_device_width(cs) == 16) {
+				/*
+				 * 16bit mem device can be twice more - no need
+				 * in less significant pin
+				 */
+				max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE * 2;
+			}
+
+			if (physical_mem_size > max_mem_size) {
+				cs_mem_size = max_mem_size *
+					(ddr3_get_bus_width() /
+					 ddr3_get_device_width(cs));
+				printf("Updated Physical Mem size is from 0x%x to %x\n",
+				       physical_mem_size,
+				       DEVICE_MAX_DRAM_ADDRESS_SIZE);
+			}
+#endif
+
+			/* set fast path window control for the cs */
+			reg = 0xffffe1;
+			reg |= (cs << 2);
+			reg |= (cs_mem_size - 1) & 0xffff0000;
+			/*Open fast path Window */
+			reg_write(REG_FASTPATH_WIN_CTRL_ADDR(cs), reg);
+
+			/* Set fast path window base address for the cs */
+			reg = ((cs_mem_size) * cs) & 0xffff0000;
+			/* Set base address */
+			reg_write(REG_FASTPATH_WIN_BASE_ADDR(cs), reg);
+
+			/*
+			 * Since memory size may be bigger than 4G the summ may
+			 * be more than 32 bit word,
+			 * so to estimate the result divide mem_total_size and
+			 * cs_mem_size by 0x10000 (it is equal to >> 16)
+			 */
+			mem_total_size_c = (mem_total_size >> 16) & 0xffffffffffff;
+			cs_mem_size_c = (cs_mem_size >> 16) & 0xffffffffffff;
+			/* if the sum less than 2 G - calculate the value */
+			if (mem_total_size_c + cs_mem_size_c < 0x10000)
+				mem_total_size += cs_mem_size;
+			else	/* put max possible size */
+				mem_total_size = L2_FILTER_FOR_MAX_MEMORY_SIZE;
+		}
+	}
+
+	/* Set L2 filtering to Max Memory size */
+	reg_write(ADDRESS_FILTERING_END_REGISTER, mem_total_size);
+
+	return MV_OK;
+}
+
+static int ddr3_restore_and_set_final_windows(u32 *win, const char *ddr_type)
+{
+	u32 win_ctrl_reg, num_of_win_regs;
+	u32 cs_ena = mv_ddr_sys_env_get_cs_ena_from_reg();
+	u32 ui;
+
+	win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR;
+	num_of_win_regs = 16;
+
+	/* Return XBAR windows 4-7 or 16-19 init configuration */
+	for (ui = 0; ui < num_of_win_regs; ui++)
+		reg_write((win_ctrl_reg + 0x4 * ui), win[ui]);
+
+	printf("%s Training Sequence - Switching XBAR Window to FastPath Window\n",
+	       ddr_type);
+
+#if defined DYNAMIC_CS_SIZE_CONFIG
+	if (ddr3_fast_path_dynamic_cs_size_config(cs_ena) != MV_OK)
+		printf("ddr3_fast_path_dynamic_cs_size_config FAILED\n");
+#else
+	u32 reg, cs;
+	reg = 0x1fffffe1;
+	for (cs = 0; cs < MAX_CS_NUM; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg |= (cs << 2);
+			break;
+		}
+	}
+	/* Open fast path Window to - 0.5G */
+	reg_write(REG_FASTPATH_WIN_CTRL_ADDR(0), reg);
+#endif
+
+	return MV_OK;
+}
+
+static int ddr3_save_and_set_training_windows(u32 *win)
+{
+	u32 cs_ena;
+	u32 reg, tmp_count, cs, ui;
+	u32 win_ctrl_reg, win_base_reg, win_remap_reg;
+	u32 num_of_win_regs, win_jump_index;
+	win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR;
+	win_base_reg = REG_XBAR_WIN_4_BASE_ADDR;
+	win_remap_reg = REG_XBAR_WIN_4_REMAP_ADDR;
+	win_jump_index = 0x10;
+	num_of_win_regs = 16;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+#ifdef DISABLE_L2_FILTERING_DURING_DDR_TRAINING
+	/*
+	 * Disable L2 filtering during DDR training
+	 * (when Cross Bar window is open)
+	 */
+	reg_write(ADDRESS_FILTERING_END_REGISTER, 0);
+#endif
+
+	cs_ena = tm->interface_params[0].as_bus_params[0].cs_bitmask;
+
+	/* Close XBAR Window 19 - Not needed */
+	/* {0x000200e8}  -   Open Mbus Window - 2G */
+	reg_write(REG_XBAR_WIN_19_CTRL_ADDR, 0);
+
+	/* Save XBAR Windows 4-19 init configurations */
+	for (ui = 0; ui < num_of_win_regs; ui++)
+		win[ui] = reg_read(win_ctrl_reg + 0x4 * ui);
+
+	/* Open XBAR Windows 4-7 or 16-19 for other CS */
+	reg = 0;
+	tmp_count = 0;
+	for (cs = 0; cs < MAX_CS_NUM; cs++) {
+		if (cs_ena & (1 << cs)) {
+			switch (cs) {
+			case 0:
+				reg = 0x0e00;
+				break;
+			case 1:
+				reg = 0x0d00;
+				break;
+			case 2:
+				reg = 0x0b00;
+				break;
+			case 3:
+				reg = 0x0700;
+				break;
+			}
+			reg |= (1 << 0);
+			reg |= (SDRAM_CS_SIZE & 0xffff0000);
+
+			reg_write(win_ctrl_reg + win_jump_index * tmp_count,
+				  reg);
+			reg = (((SDRAM_CS_SIZE + 1) * (tmp_count)) &
+			       0xffff0000);
+			reg_write(win_base_reg + win_jump_index * tmp_count,
+				  reg);
+
+			if (win_remap_reg <= REG_XBAR_WIN_7_REMAP_ADDR)
+				reg_write(win_remap_reg +
+					  win_jump_index * tmp_count, 0);
+
+			tmp_count++;
+		}
+	}
+
+	return MV_OK;
+}
+
+static u32 win[16];
+
+int mv_ddr_pre_training_soc_config(const char *ddr_type)
+{
+	u32 soc_num;
+	u32 reg_val;
+
+	/* Switching CPU to MRVL ID */
+	soc_num = (reg_read(REG_SAMPLE_RESET_HIGH_ADDR) & SAR1_CPU_CORE_MASK) >>
+		SAR1_CPU_CORE_OFFSET;
+	switch (soc_num) {
+	case 0x3:
+		reg_bit_set(CPU_CONFIGURATION_REG(3), CPU_MRVL_ID_OFFSET);
+		reg_bit_set(CPU_CONFIGURATION_REG(2), CPU_MRVL_ID_OFFSET);
+		/* fallthrough */
+	case 0x1:
+		reg_bit_set(CPU_CONFIGURATION_REG(1), CPU_MRVL_ID_OFFSET);
+		/* fallthrough */
+	case 0x0:
+		reg_bit_set(CPU_CONFIGURATION_REG(0), CPU_MRVL_ID_OFFSET);
+		/* fallthrough */
+	default:
+		break;
+	}
+
+	/*
+	 * Set DRAM Reset Mask in case detected GPIO indication of wakeup from
+	 * suspend i.e the DRAM values will not be overwritten / reset when
+	 * waking from suspend
+	 */
+	if (mv_ddr_sys_env_suspend_wakeup_check() ==
+	    SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED) {
+		reg_bit_set(SDRAM_INIT_CTRL_REG,
+			    DRAM_RESET_MASK_MASKED << DRAM_RESET_MASK_OFFS);
+	}
+
+	/* Check if DRAM is already initialized  */
+	if (reg_read(REG_BOOTROM_ROUTINE_ADDR) &
+	    (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS)) {
+		printf("%s Training Sequence - 2nd boot - Skip\n", ddr_type);
+		return MV_OK;
+	}
+
+	/* Fix read ready phases for all SOC in reg 0x15c8 */
+	reg_val = reg_read(TRAINING_DBG_3_REG);
+
+	reg_val &= ~(TRN_DBG_RDY_INC_PH_2TO1_MASK << TRN_DBG_RDY_INC_PH_2TO1_OFFS(0));
+	reg_val |= (0x4 << TRN_DBG_RDY_INC_PH_2TO1_OFFS(0));	/* phase 0 */
+
+	reg_val &= ~(TRN_DBG_RDY_INC_PH_2TO1_MASK << TRN_DBG_RDY_INC_PH_2TO1_OFFS(1));
+	reg_val |= (0x4 << TRN_DBG_RDY_INC_PH_2TO1_OFFS(1));	/* phase 1 */
+
+	reg_val &= ~(TRN_DBG_RDY_INC_PH_2TO1_MASK << TRN_DBG_RDY_INC_PH_2TO1_OFFS(3));
+	reg_val |= (0x6 << TRN_DBG_RDY_INC_PH_2TO1_OFFS(3));	/* phase 3 */
+
+	reg_val &= ~(TRN_DBG_RDY_INC_PH_2TO1_MASK << TRN_DBG_RDY_INC_PH_2TO1_OFFS(4));
+	reg_val |= (0x6 << TRN_DBG_RDY_INC_PH_2TO1_OFFS(4));	/* phase 4 */
+
+	reg_val &= ~(TRN_DBG_RDY_INC_PH_2TO1_MASK << TRN_DBG_RDY_INC_PH_2TO1_OFFS(5));
+	reg_val |= (0x6 << TRN_DBG_RDY_INC_PH_2TO1_OFFS(5));	/* phase 5 */
+
+	reg_write(TRAINING_DBG_3_REG, reg_val);
+
+	/*
+	 * Axi_bresp_mode[8] = Compliant,
+	 * Axi_addr_decode_cntrl[11] = Internal,
+	 * Axi_data_bus_width[0] = 128bit
+	 * */
+	/* 0x14a8 - AXI Control Register */
+	reg_write(AXI_CTRL_REG, 0);
+
+	/*
+	 * Stage 2 - Training Values Setup
+	 */
+	/* Set X-BAR windows for the training sequence */
+	ddr3_save_and_set_training_windows(win);
+
+	return MV_OK;
+}
+
+static int ddr3_new_tip_dlb_config(void)
+{
+	u32 reg, i = 0;
+	struct dlb_config *config_table_ptr = sys_env_dlb_config_ptr_get();
+
+	/* Write the configuration */
+	while (config_table_ptr[i].reg_addr != 0) {
+		reg_write(config_table_ptr[i].reg_addr,
+			  config_table_ptr[i].reg_data);
+		i++;
+	}
+
+
+	/* Enable DLB */
+	reg = reg_read(DLB_CTRL_REG);
+	reg &= ~(DLB_EN_MASK << DLB_EN_OFFS) &
+	       ~(WR_COALESCE_EN_MASK << WR_COALESCE_EN_OFFS) &
+	       ~(AXI_PREFETCH_EN_MASK << AXI_PREFETCH_EN_OFFS) &
+	       ~(MBUS_PREFETCH_EN_MASK << MBUS_PREFETCH_EN_OFFS) &
+	       ~(PREFETCH_NXT_LN_SZ_TRIG_MASK << PREFETCH_NXT_LN_SZ_TRIG_OFFS);
+
+	reg |= (DLB_EN_ENA << DLB_EN_OFFS) |
+	       (WR_COALESCE_EN_ENA << WR_COALESCE_EN_OFFS) |
+	       (AXI_PREFETCH_EN_ENA << AXI_PREFETCH_EN_OFFS) |
+	       (MBUS_PREFETCH_EN_ENA << MBUS_PREFETCH_EN_OFFS) |
+	       (PREFETCH_NXT_LN_SZ_TRIG_ENA << PREFETCH_NXT_LN_SZ_TRIG_OFFS);
+
+	reg_write(DLB_CTRL_REG, reg);
+
+	return MV_OK;
+}
+
+int mv_ddr_post_training_soc_config(const char *ddr_type)
+{
+	u32 reg_val;
+
+	/* Restore and set windows */
+	ddr3_restore_and_set_final_windows(win, ddr_type);
+
+	/* Update DRAM init indication in bootROM register */
+	reg_val = reg_read(REG_BOOTROM_ROUTINE_ADDR);
+	reg_write(REG_BOOTROM_ROUTINE_ADDR,
+		  reg_val | (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS));
+
+	/* DLB config */
+	ddr3_new_tip_dlb_config();
+
+	return MV_OK;
+}
+
+void mv_ddr_mc_config(void)
+{
+	/* Memory controller initializations */
+	struct init_cntr_param init_param;
+	int status;
+
+	init_param.do_mrs_phy = 1;
+	init_param.is_ctrl64_bit = 0;
+	init_param.init_phy = 1;
+	init_param.msys_init = 1;
+	status = hws_ddr3_tip_init_controller(0, &init_param);
+	if (status != MV_OK)
+		printf("DDR3 init controller - FAILED 0x%x\n", status);
+
+	status = mv_ddr_mc_init();
+	if (status != MV_OK)
+		printf("DDR3 init_sequence - FAILED 0x%x\n", status);
+}
+/* function: mv_ddr_mc_init
+ * this function enables the dunit after init controller configuration
+ */
+int mv_ddr_mc_init(void)
+{
+	CHECK_STATUS(ddr3_tip_enable_init_sequence(0));
+
+	return MV_OK;
+}
+
+/* function: ddr3_tip_configure_phy
+ * configures phy and electrical parameters
+ */
+int ddr3_tip_configure_phy(u32 dev_num)
+{
+	u32 if_id, phy_id;
+	u32 octets_per_if_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA,
+		PAD_ZRI_CAL_PHY_REG,
+		((0x7f & g_zpri_data) << 7 | (0x7f & g_znri_data))));
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_CONTROL,
+		PAD_ZRI_CAL_PHY_REG,
+		((0x7f & g_zpri_ctrl) << 7 | (0x7f & g_znri_ctrl))));
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA,
+		PAD_ODT_CAL_PHY_REG,
+		((0x3f & g_zpodt_data) << 6 | (0x3f & g_znodt_data))));
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_CONTROL,
+		PAD_ODT_CAL_PHY_REG,
+		((0x3f & g_zpodt_ctrl) << 6 | (0x3f & g_znodt_ctrl))));
+
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA,
+		PAD_PRE_DISABLE_PHY_REG, 0));
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA,
+		CMOS_CONFIG_PHY_REG, 0));
+	CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_CONTROL,
+		CMOS_CONFIG_PHY_REG, 0));
+
+	for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
+		/* check if the interface is enabled */
+		VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
+
+		for (phy_id = 0;
+			phy_id < octets_per_if_num;
+			phy_id++) {
+				VALIDATE_BUS_ACTIVE(tm->bus_act_mask, phy_id);
+				/* Vref & clamp */
+				CHECK_STATUS(ddr3_tip_bus_read_modify_write
+					(dev_num, ACCESS_TYPE_UNICAST,
+					if_id, phy_id, DDR_PHY_DATA,
+					PAD_CFG_PHY_REG,
+					((clamp_tbl[if_id] << 4) | vref_init_val),
+					((0x7 << 4) | 0x7)));
+				/* clamp not relevant for control */
+				CHECK_STATUS(ddr3_tip_bus_read_modify_write
+					(dev_num, ACCESS_TYPE_UNICAST,
+					if_id, phy_id, DDR_PHY_CONTROL,
+					PAD_CFG_PHY_REG, 0x4, 0x7));
+		}
+	}
+
+	if (ddr3_tip_dev_attr_get(dev_num, MV_ATTR_PHY_EDGE) ==
+		MV_DDR_PHY_EDGE_POSITIVE)
+		CHECK_STATUS(ddr3_tip_bus_write
+		(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
+		DDR_PHY_DATA, 0x90, 0x6002));
+
+
+	return MV_OK;
+}
+
+
+int mv_ddr_manual_cal_do(void)
+{
+	return 0;
+}
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_plat.h b/drivers/ddr/marvell/a38x/mv_ddr_plat.h
new file mode 100644
index 0000000..281d4c2
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_plat.h
@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _MV_DDR_PLAT_H
+#define _MV_DDR_PLAT_H
+
+#define MAX_DEVICE_NUM			1
+#define MAX_INTERFACE_NUM		1
+#define MAX_BUS_NUM			5
+#define DDR_IF_CTRL_SUBPHYS_NUM		3
+
+#define DFS_LOW_FREQ_VALUE		120
+#define SDRAM_CS_SIZE			0xfffffff	/* FIXME: implement a function for cs size for each platform */
+
+#define INTER_REGS_BASE			SOC_REGS_PHY_BASE
+#define AP_INT_REG_START_ADDR		0xd0000000
+#define AP_INT_REG_END_ADDR		0xd0100000
+
+/* Controler bus divider 1 for 32 bit, 2 for 64 bit */
+#define DDR_CONTROLLER_BUS_WIDTH_MULTIPLIER	1
+
+/* Tune internal training params values */
+#define TUNE_TRAINING_PARAMS_CK_DELAY		160
+#define TUNE_TRAINING_PARAMS_PHYREG3VAL		0xA
+#define TUNE_TRAINING_PARAMS_PRI_DATA		123
+#define TUNE_TRAINING_PARAMS_NRI_DATA		123
+#define TUNE_TRAINING_PARAMS_PRI_CTRL		74
+#define TUNE_TRAINING_PARAMS_NRI_CTRL		74
+#define TUNE_TRAINING_PARAMS_P_ODT_DATA		45
+#define TUNE_TRAINING_PARAMS_N_ODT_DATA		45
+#define TUNE_TRAINING_PARAMS_P_ODT_CTRL		45
+#define TUNE_TRAINING_PARAMS_N_ODT_CTRL		45
+#define TUNE_TRAINING_PARAMS_DIC		0x2
+#define TUNE_TRAINING_PARAMS_ODT_CONFIG_2CS	0x120012
+#define TUNE_TRAINING_PARAMS_ODT_CONFIG_1CS	0x10000
+#define TUNE_TRAINING_PARAMS_RTT_NOM		0x44
+
+#define TUNE_TRAINING_PARAMS_RTT_WR_1CS		0x0   /*off*/
+#define TUNE_TRAINING_PARAMS_RTT_WR_2CS		0x0   /*off*/
+
+#define MARVELL_BOARD				MARVELL_BOARD_ID_BASE
+
+
+#define REG_DEVICE_SAR1_ADDR			0xe4204
+#define RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET	17
+#define RST2_CPU_DDR_CLOCK_SELECT_IN_MASK	0x1f
+#define DEVICE_SAMPLE_AT_RESET2_REG		0x18604
+
+#define DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_OFFSET	0
+#define DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_25MHZ	0
+#define DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_40MHZ	1
+
+/* DRAM Windows */
+#define REG_XBAR_WIN_5_CTRL_ADDR		0x20050
+#define REG_XBAR_WIN_5_BASE_ADDR		0x20054
+
+/* DRAM Windows */
+#define REG_XBAR_WIN_4_CTRL_ADDR                0x20040
+#define REG_XBAR_WIN_4_BASE_ADDR                0x20044
+#define REG_XBAR_WIN_4_REMAP_ADDR               0x20048
+#define REG_XBAR_WIN_7_REMAP_ADDR               0x20078
+#define REG_XBAR_WIN_16_CTRL_ADDR               0x200d0
+#define REG_XBAR_WIN_16_BASE_ADDR               0x200d4
+#define REG_XBAR_WIN_16_REMAP_ADDR              0x200dc
+#define REG_XBAR_WIN_19_CTRL_ADDR               0x200e8
+
+#define REG_FASTPATH_WIN_BASE_ADDR(win)         (0x20180 + (0x8 * win))
+#define REG_FASTPATH_WIN_CTRL_ADDR(win)         (0x20184 + (0x8 * win))
+
+#define CPU_CONFIGURATION_REG(id)	(0x21800 + (id * 0x100))
+#define CPU_MRVL_ID_OFFSET		0x10
+#define SAR1_CPU_CORE_MASK		0x00000018
+#define SAR1_CPU_CORE_OFFSET		3
+
+/* SatR defined too change topology busWidth and ECC configuration */
+#define DDR_SATR_CONFIG_MASK_WIDTH		0x8
+#define DDR_SATR_CONFIG_MASK_ECC		0x10
+#define DDR_SATR_CONFIG_MASK_ECC_PUP		0x20
+
+#define	REG_SAMPLE_RESET_HIGH_ADDR		0x18600
+
+#define MV_BOARD_REFCLK_25MHZ			25000000
+#define MV_BOARD_REFCLK				MV_BOARD_REFCLK_25MHZ
+
+#define MAX_DQ_NUM				40
+
+/* dram line buffer registers */
+#define DLB_CTRL_REG			0x1700
+#define DLB_EN_OFFS			0
+#define DLB_EN_MASK			0x1
+#define DLB_EN_ENA			1
+#define DLB_EN_DIS			0
+#define WR_COALESCE_EN_OFFS		2
+#define WR_COALESCE_EN_MASK		0x1
+#define WR_COALESCE_EN_ENA		1
+#define WR_COALESCE_EN_DIS		0
+#define AXI_PREFETCH_EN_OFFS		3
+#define AXI_PREFETCH_EN_MASK		0x1
+#define AXI_PREFETCH_EN_ENA		1
+#define AXI_PREFETCH_EN_DIS		0
+#define MBUS_PREFETCH_EN_OFFS		4
+#define MBUS_PREFETCH_EN_MASK		0x1
+#define MBUS_PREFETCH_EN_ENA		1
+#define MBUS_PREFETCH_EN_DIS		0
+#define PREFETCH_NXT_LN_SZ_TRIG_OFFS	6
+#define PREFETCH_NXT_LN_SZ_TRIG_MASK	0x1
+#define PREFETCH_NXT_LN_SZ_TRIG_ENA	1
+#define PREFETCH_NXT_LN_SZ_TRIG_DIS	0
+
+#define DLB_BUS_OPT_WT_REG		0x1704
+#define DLB_AGING_REG			0x1708
+#define DLB_EVICTION_CTRL_REG		0x170c
+#define DLB_EVICTION_TIMERS_REG		0x1710
+#define DLB_USER_CMD_REG		0x1714
+#define DLB_WTS_DIFF_CS_REG		0x1770
+#define DLB_WTS_DIFF_BG_REG		0x1774
+#define DLB_WTS_SAME_BG_REG		0x1778
+#define DLB_WTS_CMDS_REG		0x177c
+#define DLB_WTS_ATTR_PRIO_REG		0x1780
+#define DLB_QUEUE_MAP_REG		0x1784
+#define DLB_SPLIT_REG			0x1788
+
+/* ck swap control subphy number */
+#define CK_SWAP_CTRL_PHY_NUM	2
+
+/* Subphy result control per byte registers */
+#define RESULT_CONTROL_BYTE_PUP_0_REG		0x1830
+#define RESULT_CONTROL_BYTE_PUP_1_REG		0x1834
+#define RESULT_CONTROL_BYTE_PUP_2_REG		0x1838
+#define RESULT_CONTROL_BYTE_PUP_3_REG		0x183c
+#define RESULT_CONTROL_BYTE_PUP_4_REG		0x18b0
+
+/* Subphy result control per bit registers */
+#define RESULT_CONTROL_PUP_0_BIT_0_REG		0x18b4
+#define RESULT_CONTROL_PUP_0_BIT_1_REG		0x18b8
+#define RESULT_CONTROL_PUP_0_BIT_2_REG		0x18bc
+#define RESULT_CONTROL_PUP_0_BIT_3_REG		0x18c0
+#define RESULT_CONTROL_PUP_0_BIT_4_REG		0x18c4
+#define RESULT_CONTROL_PUP_0_BIT_5_REG		0x18c8
+#define RESULT_CONTROL_PUP_0_BIT_6_REG		0x18cc
+#define RESULT_CONTROL_PUP_0_BIT_7_REG		0x18f0
+
+#define RESULT_CONTROL_PUP_1_BIT_0_REG		0x18f4
+#define RESULT_CONTROL_PUP_1_BIT_1_REG		0x18f8
+#define RESULT_CONTROL_PUP_1_BIT_2_REG		0x18fc
+#define RESULT_CONTROL_PUP_1_BIT_3_REG		0x1930
+#define RESULT_CONTROL_PUP_1_BIT_4_REG		0x1934
+#define RESULT_CONTROL_PUP_1_BIT_5_REG		0x1938
+#define RESULT_CONTROL_PUP_1_BIT_6_REG		0x193c
+#define RESULT_CONTROL_PUP_1_BIT_7_REG		0x19b0
+
+#define RESULT_CONTROL_PUP_2_BIT_0_REG		0x19b4
+#define RESULT_CONTROL_PUP_2_BIT_1_REG		0x19b8
+#define RESULT_CONTROL_PUP_2_BIT_2_REG		0x19bc
+#define RESULT_CONTROL_PUP_2_BIT_3_REG		0x19c0
+#define RESULT_CONTROL_PUP_2_BIT_4_REG		0x19c4
+#define RESULT_CONTROL_PUP_2_BIT_5_REG		0x19c8
+#define RESULT_CONTROL_PUP_2_BIT_6_REG		0x19cc
+#define RESULT_CONTROL_PUP_2_BIT_7_REG		0x19f0
+
+#define RESULT_CONTROL_PUP_3_BIT_0_REG		0x19f4
+#define RESULT_CONTROL_PUP_3_BIT_1_REG		0x19f8
+#define RESULT_CONTROL_PUP_3_BIT_2_REG		0x19fc
+#define RESULT_CONTROL_PUP_3_BIT_3_REG		0x1a30
+#define RESULT_CONTROL_PUP_3_BIT_4_REG		0x1a34
+#define RESULT_CONTROL_PUP_3_BIT_5_REG		0x1a38
+#define RESULT_CONTROL_PUP_3_BIT_6_REG		0x1a3c
+#define RESULT_CONTROL_PUP_3_BIT_7_REG		0x1ab0
+
+#define RESULT_CONTROL_PUP_4_BIT_0_REG		0x1ab4
+#define RESULT_CONTROL_PUP_4_BIT_1_REG		0x1ab8
+#define RESULT_CONTROL_PUP_4_BIT_2_REG		0x1abc
+#define RESULT_CONTROL_PUP_4_BIT_3_REG		0x1ac0
+#define RESULT_CONTROL_PUP_4_BIT_4_REG		0x1ac4
+#define RESULT_CONTROL_PUP_4_BIT_5_REG		0x1ac8
+#define RESULT_CONTROL_PUP_4_BIT_6_REG		0x1acc
+#define RESULT_CONTROL_PUP_4_BIT_7_REG		0x1af0
+
+/* CPU */
+#define REG_BOOTROM_ROUTINE_ADDR		0x182d0
+#define REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS	12
+
+/* Matrix enables DRAM modes (bus width/ECC) per boardId */
+#define TOPOLOGY_UPDATE_32BIT			0
+#define TOPOLOGY_UPDATE_32BIT_ECC		1
+#define TOPOLOGY_UPDATE_16BIT			2
+#define TOPOLOGY_UPDATE_16BIT_ECC		3
+#define TOPOLOGY_UPDATE_16BIT_ECC_PUP3		4
+#define TOPOLOGY_UPDATE { \
+		/* 32Bit, 32bit ECC, 16bit, 16bit ECC PUP4, 16bit ECC PUP3 */ \
+		{1, 1, 1, 1, 1},	/* RD_NAS_68XX_ID */ \
+		{1, 1, 1, 1, 1},	/* DB_68XX_ID	  */ \
+		{1, 0, 1, 0, 1},	/* RD_AP_68XX_ID  */ \
+		{1, 0, 1, 0, 1},	/* DB_AP_68XX_ID  */ \
+		{1, 0, 1, 0, 1},	/* DB_GP_68XX_ID  */ \
+		{0, 0, 1, 1, 0},	/* DB_BP_6821_ID  */ \
+		{1, 1, 1, 1, 1}		/* DB_AMC_6820_ID */ \
+	};
+
+enum {
+	CPU_1066MHZ_DDR_400MHZ,
+	CPU_RESERVED_DDR_RESERVED0,
+	CPU_667MHZ_DDR_667MHZ,
+	CPU_800MHZ_DDR_800MHZ,
+	CPU_RESERVED_DDR_RESERVED1,
+	CPU_RESERVED_DDR_RESERVED2,
+	CPU_RESERVED_DDR_RESERVED3,
+	LAST_FREQ
+};
+
+/* struct used for DLB configuration array */
+struct dlb_config {
+	u32 reg_addr;
+	u32 reg_data;
+};
+
+#define ACTIVE_INTERFACE_MASK			0x1
+
+extern u32 dmin_phy_reg_table[][2];
+extern u16 odt_slope[];
+extern u16 odt_intercept[];
+
+int mv_ddr_pre_training_soc_config(const char *ddr_type);
+int mv_ddr_post_training_soc_config(const char *ddr_type);
+void mv_ddr_mem_scrubbing(void);
+u32 mv_ddr_init_freq_get(void);
+void mv_ddr_odpg_enable(void);
+void mv_ddr_odpg_disable(void);
+void mv_ddr_odpg_done_clr(void);
+int mv_ddr_is_odpg_done(u32 count);
+void mv_ddr_training_enable(void);
+int mv_ddr_is_training_done(u32 count, u32 *result);
+u32 mv_ddr_dm_pad_get(void);
+int mv_ddr_pre_training_fixup(void);
+int mv_ddr_post_training_fixup(void);
+int mv_ddr_manual_cal_do(void);
+int ddr3_calc_mem_cs_size(u32 cs, uint64_t *cs_size);
+
+#endif /* _MV_DDR_PLAT_H */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_regs.h b/drivers/ddr/marvell/a38x/mv_ddr_regs.h
new file mode 100644
index 0000000..cf2a6c9
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_regs.h
@@ -0,0 +1,465 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _MV_DDR_REGS_H
+#define _MV_DDR_REGS_H
+
+#define GLOB_CTRL_STATUS_REG			0x1030
+#define TRAINING_TRIGGER_OFFS			0
+#define TRAINING_TRIGGER_MASK			0x1
+#define TRAINING_TRIGGER_ENA			1
+#define TRAINING_DONE_OFFS			1
+#define TRAINING_DONE_MASK			0x1
+#define TRAINING_DONE_DONE			1
+#define TRAINING_DONE_NOT_DONE			0
+#define TRAINING_RESULT_OFFS			2
+#define TRAINING_RESULT_MASK			0x1
+#define TRAINING_RESULT_PASS			0
+#define TRAINING_RESULT_FAIL			1
+
+#define GENERAL_TRAINING_OPCODE_REG		0x1034
+
+#define OPCODE_REG0_BASE			0x1038
+#define OPCODE_REG0_REG(obj)			(OPCODE_REG0_BASE + (obj) * 0x4)
+
+#define OPCODE_REG1_BASE			0x10b0
+#define OPCODE_REG1_REG(obj)			(OPCODE_REG1_BASE + (obj) * 0x4)
+
+#define CAL_PHY_BASE				0x10c0
+#define CAL_PHY_REG(obj)			(CAL_PHY_BASE + (obj) * 0x4)
+
+#define WL_DONE_CNTR_REF_REG			0x10f8
+#define ODPG_WR_RD_MODE_ENA_REG			0x10fc
+
+#define SDRAM_CFG_REG				0x1400
+#define REFRESH_OFFS				0
+#define REFRESH_MASK				0x3fff
+#define DRAM_TYPE_OFFS				14
+#define DRAM_TYPE_MASK				0x1
+#define BUS_IN_USE_OFFS				15
+#define BUS_IN_USE_MASK				0x1
+#define CPU_2DRAM_WR_BUFF_CUT_TH_OFFS		16
+#define CPU_2DRAM_WR_BUFF_CUT_TH_MASK		0x1
+#define REG_DIMM_OFFS				17
+#define REG_DIMM_MASK				0x1
+#define ECC_OFFS				18
+#define ECC_MASK				0x1
+#define IGNORE_ERRORS_OFFS			19
+#define IGNORE_ERRORS_MASK			0x1
+#define DRAM_TYPE_HIGH_OFFS			20
+#define DRAM_TYPE_HIGH_MASK			0x1
+#define SELF_REFRESH_MODE_OFFS			24
+#define SELF_REFRESH_MODE_MASK			0x1
+#define CPU_RD_PER_PROP_OFFS			25
+#define CPU_RD_PER_PROP_MASK			0x1
+#define DDR4_EMULATION_OFFS			26
+#define DDR4_EMULATION_MASK			0x1
+#define PHY_RF_RST_OFFS				27
+#define PHY_RF_RST_MASK				0x1
+#define PUP_RST_DIVIDER_OFFS			28
+#define PUP_RST_DIVIDER_MASK			0x1
+#define DATA_PUP_WR_RESET_OFFS			29
+#define DATA_PUP_WR_RESET_MASK			0x1
+#define DATA_PUP_RD_RESET_OFFS			30
+#define DATA_PUP_RD_RESET_MASK			0x1
+#define DATA_PUP_RD_RESET_ENA			0x0
+#define DATA_PUP_RD_RESET_DIS			0x1
+#define IO_BIST_OFFS				31
+#define DATA_PUP_RD_RESET_MASK			0x1
+
+#define DUNIT_CTRL_LOW_REG			0x1404
+
+#define SDRAM_TIMING_LOW_REG			0x1408
+#define SDRAM_TIMING_LOW_TRAS_OFFS		0
+#define SDRAM_TIMING_LOW_TRAS_MASK		0xf
+#define SDRAM_TIMING_LOW_TRCD_OFFS		4
+#define SDRAM_TIMING_LOW_TRCD_MASK		0xf
+#define SDRAM_TIMING_HIGH_TRCD_OFFS		22
+#define SDRAM_TIMING_HIGH_TRCD_MASK		0x1
+#define SDRAM_TIMING_LOW_TRP_OFFS		8
+#define SDRAM_TIMING_LOW_TRP_MASK		0xf
+#define SDRAM_TIMING_HIGH_TRP_OFFS		23
+#define SDRAM_TIMING_HIGH_TRP_MASK		0x1
+#define SDRAM_TIMING_LOW_TWR_OFFS		12
+#define SDRAM_TIMING_LOW_TWR_MASK		0xf
+#define SDRAM_TIMING_LOW_TWTR_OFFS		16
+#define SDRAM_TIMING_LOW_TWTR_MASK		0xf
+#define SDRAM_TIMING_LOW_TRAS_HIGH_OFFS		20
+#define SDRAM_TIMING_LOW_TRAS_HIGH_MASK		0x3
+#define SDRAM_TIMING_LOW_TRRD_OFFS		24
+#define SDRAM_TIMING_LOW_TRRD_MASK		0xf
+#define SDRAM_TIMING_LOW_TRTP_OFFS		28
+#define SDRAM_TIMING_LOW_TRTP_MASK		0xf
+
+#define SDRAM_TIMING_HIGH_REG			0x140c
+#define SDRAM_TIMING_HIGH_TRFC_OFFS		0
+#define SDRAM_TIMING_HIGH_TRFC_MASK		0x7f
+#define SDRAM_TIMING_HIGH_TR2R_OFFS		7
+#define SDRAM_TIMING_HIGH_TR2R_MASK		0x3
+#define SDRAM_TIMING_HIGH_TR2W_W2R_OFFS		9
+#define SDRAM_TIMING_HIGH_TR2W_W2R_MASK		0x3
+#define SDRAM_TIMING_HIGH_TW2W_OFFS		11
+#define SDRAM_TIMING_HIGH_TW2W_MASK		0x1f
+#define SDRAM_TIMING_HIGH_TRFC_HIGH_OFFS	16
+#define SDRAM_TIMING_HIGH_TRFC_HIGH_MASK	0x7
+#define SDRAM_TIMING_HIGH_TR2R_HIGH_OFFS	19
+#define SDRAM_TIMING_HIGH_TR2R_HIGH_MASK	0x7
+#define SDRAM_TIMING_HIGH_TR2W_W2R_HIGH_OFFS	22
+#define SDRAM_TIMING_HIGH_TR2W_W2R_HIGH_MASK	0x7
+#define SDRAM_TIMING_HIGH_TMOD_OFFS		25
+#define SDRAM_TIMING_HIGH_TMOD_MASK		0xf
+#define SDRAM_TIMING_HIGH_TMOD_HIGH_OFFS	30
+#define SDRAM_TIMING_HIGH_TMOD_HIGH_MASK	0x3
+
+#define SDRAM_ADDR_CTRL_REG			0x1410
+#define CS_STRUCT_BASE				0
+#define CS_STRUCT_OFFS(cs)			(CS_STRUCT_BASE + (cs) * 4)
+#define CS_STRUCT_MASK				0x3
+#define CS_SIZE_BASE				2
+#define CS_SIZE_OFFS(cs)			(CS_SIZE_BASE + (cs) * 4)
+#define CS_SIZE_MASK				0x3
+#define CS_SIZE_HIGH_BASE			20
+#define CS_SIZE_HIGH_OFFS(cs)			(CS_SIZE_HIGH_BASE + (cs))
+#define CS_SIZE_HIGH_MASK			0x1
+#define T_FAW_OFFS				24
+#define T_FAW_MASK				0x7f
+
+#define SDRAM_OPEN_PAGES_CTRL_REG		0x1414
+
+#define SDRAM_OP_REG				0x1418
+#define SDRAM_OP_CMD_OFFS			0
+#define SDRAM_OP_CMD_MASK			0x1f
+#define SDRAM_OP_CMD_CS_BASE			8
+#define SDRAM_OP_CMD_CS_OFFS(cs)		(SDRAM_OP_CMD_CS_BASE + (cs))
+#define SDRAM_OP_CMD_CS_MASK			0x1
+#define SDRAM_OP_CMD_ALL_CS_MASK		0xf
+enum {
+	CMD_NORMAL,
+	CMD_PRECHARGE,
+	CMD_REFRESH,
+	CMD_DDR3_DDR4_MR0,
+	CMD_DDR3_DDR4_MR1,
+	CMD_NOP,
+	CMD_RES_0X6,
+	CMD_SELFREFRESH,
+	CMD_DDR3_DDR4_MR2,
+	CMD_DDR3_DDR4_MR3,
+	CMD_ACT_PDE,
+	CMD_PRE_PDE,
+	CMD_ZQCL,
+	CMD_ZQCS,
+	CMD_CWA,
+	CMD_RES_0XF,
+	CMD_DDR4_MR4,
+	CMD_DDR4_MR5,
+	CMD_DDR4_MR6,
+	DDR4_MPR_WR
+};
+
+#define DUNIT_CTRL_HIGH_REG			0x1424
+#define CPU_INTERJECTION_ENA_OFFS		3
+#define CPU_INTERJECTION_ENA_MASK		0x1
+#define CPU_INTERJECTION_ENA_SPLIT_ENA		0
+#define CPU_INTERJECTION_ENA_SPLIT_DIS		1
+
+#define DDR_ODT_TIMING_LOW_REG			0x1428
+
+#define DDR_TIMING_REG				0x142c
+#define DDR_TIMING_TCCD_OFFS			18
+#define DDR_TIMING_TCCD_MASK			0x7
+#define DDR_TIMING_TPD_OFFS			0
+#define DDR_TIMING_TPD_MASK			0xf
+#define DDR_TIMING_TXPDLL_OFFS			4
+#define DDR_TIMING_TXPDLL_MASK			0x1f
+
+#define DDR_ODT_TIMING_HIGH_REG			0x147c
+
+#define SDRAM_INIT_CTRL_REG			0x1480
+#define DRAM_RESET_MASK_OFFS			1
+#define DRAM_RESET_MASK_MASK			0x1
+#define DRAM_RESET_MASK_NORMAL			0
+#define DRAM_RESET_MASK_MASKED			1
+
+#define SDRAM_ODT_CTRL_HIGH_REG			0x1498
+#define DUNIT_ODT_CTRL_REG			0x149c
+#define RD_BUFFER_SEL_REG			0x14a4
+#define AXI_CTRL_REG				0x14a8
+#define DUNIT_MMASK_REG				0x14b0
+
+#define HORZ_SSTL_CAL_MACH_CTRL_REG		0x14c8
+#define HORZ_POD_CAL_MACH_CTRL_REG		0x17c8
+#define VERT_SSTL_CAL_MACH_CTRL_REG		0x1dc8
+#define VERT_POD_CAL_MACH_CTRL_REG		0x1ec8
+
+#define MAIN_PADS_CAL_MACH_CTRL_REG		0x14cc
+#define DYN_PADS_CAL_ENABLE_OFFS		0
+#define DYN_PADS_CAL_ENABLE_MASK		0x1
+#define DYN_PADS_CAL_ENABLE_DIS			0
+#define DYN_PADS_CAL_ENABLE_ENA			1
+#define PADS_RECAL_OFFS				1
+#define PADS_RECAL_MASK				0x1
+#define DYN_PADS_CAL_BLOCK_OFFS			2
+#define DYN_PADS_CAL_BLOCK_MASK			0x1
+#define CAL_UPDATE_CTRL_OFFS			3
+#define CAL_UPDATE_CTRL_MASK			0x3
+#define CAL_UPDATE_CTRL_INT			1
+#define CAL_UPDATE_CTRL_EXT			2
+#define DYN_PADS_CAL_CNTR_OFFS			13
+#define DYN_PADS_CAL_CNTR_MASK			0x3ffff
+#define CAL_MACH_STATUS_OFFS			31
+#define CAL_MACH_STATUS_MASK			0x1
+#define CAL_MACH_BUSY				0
+#define CAL_MACH_RDY				1
+
+#define DRAM_DLL_TIMING_REG			0x14e0
+#define DRAM_ZQ_INIT_TIMIMG_REG			0x14e4
+#define DRAM_ZQ_TIMING_REG			0x14e8
+
+#define DRAM_LONG_TIMING_REG			0x14ec
+#define DDR4_TRRD_L_OFFS			0
+#define DDR4_TRRD_L_MASK			0xf
+#define DDR4_TWTR_L_OFFS			4
+#define DDR4_TWTR_L_MASK			0xf
+
+#define DDR_IO_REG				0x1524
+#define DFS_REG					0x1528
+
+#define RD_DATA_SMPL_DLYS_REG			0x1538
+#define RD_SMPL_DLY_CS_BASE			0
+#define RD_SMPL_DLY_CS_OFFS(cs)			(RD_SMPL_DLY_CS_BASE + (cs) * 8)
+#define RD_SMPL_DLY_CS_MASK			0x1f
+
+#define RD_DATA_RDY_DLYS_REG			0x153c
+#define RD_RDY_DLY_CS_BASE			0
+#define RD_RDY_DLY_CS_OFFS(cs)			(RD_RDY_DLY_CS_BASE + (cs) * 8)
+#define RD_RDY_DLY_CS_MASK			0x1f
+
+#define TRAINING_REG				0x15b0
+#define TRN_START_OFFS				31
+#define TRN_START_MASK				0x1
+#define TRN_START_ENA				1
+#define TRN_START_DIS				0
+
+#define TRAINING_SW_1_REG			0x15b4
+
+#define TRAINING_SW_2_REG			0x15b8
+#define TRAINING_ECC_MUX_OFFS			1
+#define TRAINING_ECC_MUX_MASK			0x1
+#define TRAINING_ECC_MUX_DIS			0
+#define TRAINING_ECC_MUX_ENA			1
+#define TRAINING_SW_OVRD_OFFS			0
+#define TRAINING_SW_OVRD_MASK			0x1
+#define TRAINING_SW_OVRD_DIS			0
+#define TRAINING_SW_OVRD_ENA			1
+
+#define TRAINING_PATTERN_BASE_ADDR_REG		0x15bc
+#define TRAINING_DBG_1_REG			0x15c0
+#define TRAINING_DBG_2_REG			0x15c4
+
+#define TRAINING_DBG_3_REG			0x15c8
+#define TRN_DBG_RDY_INC_PH_2TO1_BASE		0
+#define TRN_DBG_RDY_INC_PH_2TO1_OFFS(phase)	(TRN_DBG_RDY_INC_PH_2TO1_BASE + (phase) * 3)
+#define TRN_DBG_RDY_INC_PH_2TO1_MASK		0x7
+
+#define DDR3_RANK_CTRL_REG			0x15e0
+#define CS_EXIST_BASE				0
+#define CS_EXIST_OFFS(cs)			(CS_EXIST_BASE + (cs))
+#define CS_EXIST_MASK				0x1
+
+#define ZQC_CFG_REG				0x15e4
+#define DRAM_PHY_CFG_REG			0x15ec
+#define ODPG_CTRL_CTRL_REG			0x1600
+#define ODPG_CTRL_AUTO_REFRESH_OFFS		21
+#define ODPG_CTRL_AUTO_REFRESH_MASK		0x1
+#define ODPG_CTRL_AUTO_REFRESH_DIS		1
+#define ODPG_CTRL_AUTO_REFRESH_ENA		0
+
+#define ODPG_DATA_CTRL_REG			0x1630
+#define ODPG_WRBUF_WR_CTRL_OFFS			0
+#define ODPG_WRBUF_WR_CTRL_MASK			0x1
+#define ODPG_WRBUF_WR_CTRL_DIS			0
+#define ODPG_WRBUF_WR_CTRL_ENA			1
+#define ODPG_WRBUF_RD_CTRL_OFFS			1
+#define ODPG_WRBUF_RD_CTRL_MASK			0x1
+#define ODPG_WRBUF_RD_CTRL_DIS			0
+#define ODPG_WRBUF_RD_CTRL_ENA			1
+#define ODPG_DATA_CBDEL_OFFS			15
+#define ODPG_DATA_CBDEL_MASK			0x3f
+#define ODPG_MODE_OFFS				25
+#define ODPG_MODE_MASK				0x1
+#define ODPG_MODE_RX				0
+#define ODPG_MODE_TX				1
+#define ODPG_DATA_CS_OFFS			26
+#define ODPG_DATA_CS_MASK			0x3
+#define ODPG_DISABLE_OFFS			30
+#define ODPG_DISABLE_MASK			0x1
+#define ODPG_DISABLE_DIS			1
+#define ODPG_ENABLE_OFFS			31
+#define ODPG_ENABLE_MASK			0x1
+#define ODPG_ENABLE_ENA				1
+
+#define ODPG_DATA_BUFFER_OFFS_REG		0x1638
+#define ODPG_DATA_BUFFER_SIZE_REG		0x163c
+#define PHY_LOCK_STATUS_REG			0x1674
+
+#define PHY_REG_FILE_ACCESS_REG			0x16a0
+#define PRFA_DATA_OFFS				0
+#define PRFA_DATA_MASK				0xffff
+#define PRFA_REG_NUM_OFFS			16
+#define PRFA_REG_NUM_MASK			0x3f
+#define PRFA_PUP_NUM_OFFS			22
+#define PRFA_PUP_NUM_MASK			0xf
+#define PRFA_PUP_CTRL_DATA_OFFS			26
+#define PRFA_PUP_CTRL_DATA_MASK			0x1
+#define PRFA_PUP_BCAST_WR_ENA_OFFS		27
+#define PRFA_PUP_BCAST_WR_ENA_MASK		0x1
+#define PRFA_REG_NUM_HI_OFFS			28
+#define PRFA_REG_NUM_HI_MASK			0x3
+#define PRFA_TYPE_OFFS				30
+#define PRFA_TYPE_MASK				0x1
+#define PRFA_REQ_OFFS				31
+#define PRFA_REQ_MASK				0x1
+#define PRFA_REQ_DIS				0x0
+#define PRFA_REQ_ENA				0x1
+
+#define TRAINING_WL_REG				0x16ac
+
+#define ODPG_DATA_WR_ADDR_REG			0x16b0
+#define ODPG_DATA_WR_ACK_OFFS			0
+#define ODPG_DATA_WR_ACK_MASK			0x7f
+#define ODPG_DATA_WR_DATA_OFFS			8
+#define ODPG_DATA_WR_DATA_MASK			0xff
+
+#define ODPG_DATA_WR_DATA_HIGH_REG		0x16b4
+#define ODPG_DATA_WR_DATA_LOW_REG		0x16b8
+#define ODPG_DATA_RX_WORD_ERR_ADDR_REG		0x16bc
+#define ODPG_DATA_RX_WORD_ERR_CNTR_REG		0x16c0
+#define ODPG_DATA_RX_WORD_ERR_DATA_HIGH_REG	0x16c4
+#define ODPG_DATA_RX_WORD_ERR_DATA_LOW_REG	0x16c8
+#define ODPG_DATA_WR_DATA_ERR_REG		0x16cc
+
+#define DUAL_DUNIT_CFG_REG			0x16d8
+#define FC_SAMPLE_STAGES_OFFS			0
+#define FC_SAMPLE_STAGES_MASK			0x7
+#define SINGLE_CS_PIN_OFFS			3
+#define SINGLE_CS_PIN_MASK			0x1
+#define SINGLE_CS_ENA				1
+#define TUNING_ACTIVE_SEL_OFFS			6
+#define TUNING_ACTIVE_SEL_MASK			0x1
+#define TUNING_ACTIVE_SEL_MC			0
+#define TUNING_ACTIVE_SEL_TIP			1
+
+#define WL_DQS_PATTERN_REG			0x16dc
+#define ODPG_DONE_STATUS_REG			0x16fc
+#define ODPG_DONE_STATUS_BIT_OFFS		0
+#define ODPG_DONE_STATUS_BIT_MASK		0x1
+#define ODPG_DONE_STATUS_BIT_CLR		0
+#define ODPG_DONE_STATUS_BIT_SET		1
+
+#define RESULT_CTRL_BASE			0x1830
+#define BLOCK_STATUS_OFFS			25
+#define BLOCK_STATUS_MASK			0x1
+#define BLOCK_STATUS_LOCK			1
+#define BLOCK_STATUS_NOT_LOCKED			0
+
+#define MR0_REG					0x15d0
+#define MR1_REG					0x15d4
+#define MR2_REG					0x15d8
+#define MR3_REG					0x15dc
+#define MRS0_CMD				0x3
+#define MRS1_CMD				0x4
+#define MRS2_CMD				0x8
+#define MRS3_CMD				0x9
+
+
+#define DRAM_PINS_MUX_REG			0x19d4
+#define CTRL_PINS_MUX_OFFS			0
+#define CTRL_PINS_MUX_MASK			0x3
+enum {
+	DUNIT_DDR3_ON_BOARD,
+	DUNIT_DDR3_DIMM,
+	DUNIT_DDR4_ON_BOARD,
+	DUNIT_DDR4_DIMM
+};
+
+/* ddr phy registers */
+#define WL_PHY_BASE				0x0
+#define WL_PHY_REG(cs)				(WL_PHY_BASE + (cs) * 0x4)
+#define WR_LVL_PH_SEL_OFFS			6
+#define WR_LVL_PH_SEL_MASK			0x7
+#define WR_LVL_PH_SEL_PHASE1			1
+#define WR_LVL_REF_DLY_OFFS			0
+#define WR_LVL_REF_DLY_MASK			0x1f
+#define CTRL_CENTER_DLY_OFFS			10
+#define CTRL_CENTER_DLY_MASK			0x1f
+#define CTRL_CENTER_DLY_INV_OFFS		15
+#define CTRL_CENTER_DLY_INV_MASK		0x1
+
+#define CTX_PHY_BASE				0x1
+#define CTX_PHY_REG(cs)				(CTX_PHY_BASE + (cs) * 0x4)
+
+#define RL_PHY_BASE				0x2
+#define RL_PHY_REG(cs)				(RL_PHY_BASE + (cs) * 0x4)
+#define RL_REF_DLY_OFFS				0
+#define RL_REF_DLY_MASK				0x1f
+#define RL_PH_SEL_OFFS				6
+#define RL_PH_SEL_MASK				0x7
+
+#define CRX_PHY_BASE				0x3
+#define CRX_PHY_REG(cs)				(CRX_PHY_BASE + (cs) * 0x4)
+
+#define PHY_CTRL_PHY_REG			0x90
+#define INV_PAD0_OFFS				2
+#define INV_PAD1_OFFS				3
+#define INV_PAD2_OFFS				4
+#define INV_PAD3_OFFS				5
+#define INV_PAD4_OFFS				6
+#define INV_PAD5_OFFS				7
+#define INV_PAD6_OFFS				8
+#define INV_PAD7_OFFS				9
+#define INV_PAD8_OFFS				10
+#define INV_PAD9_OFFS				11
+#define INV_PAD10_OFFS				12
+#define INV_PAD_MASK				0x1
+#define INVERT_PAD				1
+
+#define ADLL_CFG0_PHY_REG			0x92
+#define ADLL_CFG1_PHY_REG			0x93
+#define ADLL_CFG2_PHY_REG			0x94
+#define CMOS_CONFIG_PHY_REG			0xa2
+#define PAD_ZRI_CAL_PHY_REG			0xa4
+#define PAD_ODT_CAL_PHY_REG			0xa6
+#define PAD_CFG_PHY_REG				0xa8
+#define PAD_PRE_DISABLE_PHY_REG			0xa9
+#define TEST_ADLL_PHY_REG			0xbf
+
+#define VREF_PHY_BASE				0xd0
+#define VREF_PHY_REG(cs, bit)			(VREF_PHY_BASE + (cs) * 12 + bit)
+enum {
+	DQSP_PAD = 4,
+	DQSN_PAD
+};
+
+#define VREF_BCAST_PHY_BASE			0xdb
+#define VREF_BCAST_PHY_REG(cs)			(VREF_BCAST_PHY_BASE + (cs) * 12)
+
+#define PBS_TX_PHY_BASE				0x10
+#define PBS_TX_PHY_REG(cs, bit)			(PBS_TX_PHY_BASE + (cs) * 0x10 + (bit))
+
+#define PBS_TX_BCAST_PHY_BASE			0x1f
+#define PBS_TX_BCAST_PHY_REG(cs)		(PBS_TX_BCAST_PHY_BASE + (cs) * 0x10)
+
+#define PBS_RX_PHY_BASE				0x50
+#define PBS_RX_PHY_REG(cs, bit)			(PBS_RX_PHY_BASE + (cs) * 0x10 + (bit))
+
+#define PBS_RX_BCAST_PHY_BASE			0x5f
+#define PBS_RX_BCAST_PHY_REG(cs)		(PBS_RX_BCAST_PHY_BASE + (cs) * 0x10)
+
+#define RESULT_PHY_REG				0xc0
+#define RESULT_PHY_RX_OFFS			5
+#define RESULT_PHY_TX_OFFS			0
+
+
+#endif /* _MV_DDR_REGS_H */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_spd.c b/drivers/ddr/marvell/a38x/mv_ddr_spd.c
new file mode 100644
index 0000000..04dbfe9
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_spd.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "mv_ddr_spd.h"
+
+#define MV_DDR_SPD_DATA_MTB		125	/* medium timebase, ps */
+#define MV_DDR_SPD_DATA_FTB		1	/* fine timebase, ps */
+#define MV_DDR_SPD_MSB_OFFS		8	/* most significant byte offset, bits */
+
+#define MV_DDR_SPD_SUPPORTED_CLS_NUM	30
+
+static unsigned int mv_ddr_spd_supported_cls[MV_DDR_SPD_SUPPORTED_CLS_NUM];
+
+int mv_ddr_spd_supported_cls_calc(union mv_ddr_spd_data *spd_data)
+{
+	unsigned int byte, bit, start_cl;
+
+	start_cl = (spd_data->all_bytes[23] & 0x8) ? 23 : 7;
+
+	for (byte = 20; byte < 23; byte++) {
+		for (bit = 0; bit < 8; bit++) {
+			if (spd_data->all_bytes[byte] & (1 << bit))
+				mv_ddr_spd_supported_cls[(byte - 20) * 8 + bit] = start_cl + (byte - 20) * 8 + bit;
+			else
+				mv_ddr_spd_supported_cls[(byte - 20) * 8 + bit] = 0;
+		}
+	}
+
+	for (byte = 23, bit = 0; bit < 6; bit++) {
+		if (spd_data->all_bytes[byte] & (1 << bit))
+			mv_ddr_spd_supported_cls[(byte - 20) * 8 + bit] = start_cl + (byte - 20) * 8 + bit;
+		else
+			mv_ddr_spd_supported_cls[(byte - 20) * 8 + bit] = 0;
+	}
+
+	return 0;
+}
+
+unsigned int mv_ddr_spd_supported_cl_get(unsigned int cl)
+{
+	unsigned int supported_cl;
+	int i = 0;
+
+	while (i < MV_DDR_SPD_SUPPORTED_CLS_NUM &&
+		mv_ddr_spd_supported_cls[i] < cl)
+		i++;
+
+	if (i < MV_DDR_SPD_SUPPORTED_CLS_NUM)
+		supported_cl = mv_ddr_spd_supported_cls[i];
+	else
+		supported_cl = 0;
+
+	return supported_cl;
+}
+
+int mv_ddr_spd_timing_calc(union mv_ddr_spd_data *spd_data, unsigned int timing_data[])
+{
+	int calc_val;
+
+	/* t ck avg min, ps */
+	calc_val = spd_data->byte_fields.byte_18 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_125 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TCK_AVG_MIN] = calc_val;
+
+	/* t aa min, ps */
+	calc_val = spd_data->byte_fields.byte_24 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_123 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TAA_MIN] = calc_val;
+
+	/* t rfc1 min, ps */
+	timing_data[MV_DDR_TRFC1_MIN] = (spd_data->byte_fields.byte_30 +
+		(spd_data->byte_fields.byte_31 << MV_DDR_SPD_MSB_OFFS)) * MV_DDR_SPD_DATA_MTB;
+
+	/* t wr min, ps */
+	timing_data[MV_DDR_TWR_MIN] = (spd_data->byte_fields.byte_42 +
+		(spd_data->byte_fields.byte_41.bit_fields.t_wr_min_msn << MV_DDR_SPD_MSB_OFFS)) *
+		MV_DDR_SPD_DATA_MTB;
+
+	/* t rcd min, ps */
+	calc_val = spd_data->byte_fields.byte_25 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_122 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TRCD_MIN] = calc_val;
+
+	/* t rp min, ps */
+	calc_val = spd_data->byte_fields.byte_26 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_121 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TRP_MIN] = calc_val;
+
+	/* t rc min, ps */
+	calc_val = (spd_data->byte_fields.byte_29 +
+		(spd_data->byte_fields.byte_27.bit_fields.t_rc_min_msn << MV_DDR_SPD_MSB_OFFS)) *
+		MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_120 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TRC_MIN] = calc_val;
+
+	/* t ras min, ps */
+	timing_data[MV_DDR_TRAS_MIN] = (spd_data->byte_fields.byte_28 +
+		(spd_data->byte_fields.byte_27.bit_fields.t_ras_min_msn << MV_DDR_SPD_MSB_OFFS)) *
+		MV_DDR_SPD_DATA_MTB;
+
+	/* t rrd s min, ps */
+	calc_val = spd_data->byte_fields.byte_38 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_119 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TRRD_S_MIN] = calc_val;
+
+	/* t rrd l min, ps */
+	calc_val = spd_data->byte_fields.byte_39 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_118 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TRRD_L_MIN] = calc_val;
+
+	/* t ccd l min, ps */
+	calc_val = spd_data->byte_fields.byte_40 * MV_DDR_SPD_DATA_MTB +
+		(signed char)spd_data->byte_fields.byte_117 * MV_DDR_SPD_DATA_FTB;
+	if (calc_val < 0)
+		return 1;
+	timing_data[MV_DDR_TCCD_L_MIN] = calc_val;
+
+	/* t faw min, ps */
+	timing_data[MV_DDR_TFAW_MIN] = (spd_data->byte_fields.byte_37 +
+		(spd_data->byte_fields.byte_36.bit_fields.t_faw_min_msn << MV_DDR_SPD_MSB_OFFS)) *
+		MV_DDR_SPD_DATA_MTB;
+
+	/* t wtr s min, ps */
+	timing_data[MV_DDR_TWTR_S_MIN] = (spd_data->byte_fields.byte_44 +
+		(spd_data->byte_fields.byte_43.bit_fields.t_wtr_s_min_msn << MV_DDR_SPD_MSB_OFFS)) *
+		MV_DDR_SPD_DATA_MTB;
+
+	/* t wtr l min, ps */
+	timing_data[MV_DDR_TWTR_L_MIN] = (spd_data->byte_fields.byte_45 +
+		(spd_data->byte_fields.byte_43.bit_fields.t_wtr_l_min_msn << MV_DDR_SPD_MSB_OFFS)) *
+		MV_DDR_SPD_DATA_MTB;
+
+	return 0;
+}
+
+enum mv_ddr_dev_width mv_ddr_spd_dev_width_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char dev_width = spd_data->byte_fields.byte_12.bit_fields.device_width;
+	enum mv_ddr_dev_width ret_val;
+
+	switch (dev_width) {
+	case 0x00:
+		ret_val = MV_DDR_DEV_WIDTH_4BIT;
+		break;
+	case 0x01:
+		ret_val = MV_DDR_DEV_WIDTH_8BIT;
+		break;
+	case 0x02:
+		ret_val = MV_DDR_DEV_WIDTH_16BIT;
+		break;
+	case 0x03:
+		ret_val = MV_DDR_DEV_WIDTH_32BIT;
+		break;
+	default:
+		ret_val = MV_DDR_DEV_WIDTH_LAST;
+	}
+
+	return ret_val;
+}
+
+enum mv_ddr_die_capacity mv_ddr_spd_die_capacity_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char die_cap = spd_data->byte_fields.byte_4.bit_fields.die_capacity;
+	enum mv_ddr_die_capacity ret_val;
+
+	switch (die_cap) {
+	case 0x00:
+		ret_val = MV_DDR_DIE_CAP_256MBIT;
+		break;
+	case 0x01:
+		ret_val = MV_DDR_DIE_CAP_512MBIT;
+		break;
+	case 0x02:
+		ret_val = MV_DDR_DIE_CAP_1GBIT;
+		break;
+	case 0x03:
+		ret_val = MV_DDR_DIE_CAP_2GBIT;
+		break;
+	case 0x04:
+		ret_val = MV_DDR_DIE_CAP_4GBIT;
+		break;
+	case 0x05:
+		ret_val = MV_DDR_DIE_CAP_8GBIT;
+		break;
+	case 0x06:
+		ret_val = MV_DDR_DIE_CAP_16GBIT;
+		break;
+	case 0x07:
+		ret_val = MV_DDR_DIE_CAP_32GBIT;
+		break;
+	case 0x08:
+		ret_val = MV_DDR_DIE_CAP_12GBIT;
+		break;
+	case 0x09:
+		ret_val = MV_DDR_DIE_CAP_24GBIT;
+		break;
+	default:
+		ret_val = MV_DDR_DIE_CAP_LAST;
+	}
+
+	return ret_val;
+}
+
+unsigned char mv_ddr_spd_mem_mirror_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char mem_mirror = spd_data->byte_fields.byte_131.bit_fields.rank_1_mapping;
+
+	return mem_mirror;
+}
+
+enum mv_ddr_pkg_rank mv_ddr_spd_pri_bus_width_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char pri_bus_width = spd_data->byte_fields.byte_13.bit_fields.primary_bus_width;
+	enum mv_ddr_pri_bus_width ret_val;
+
+	switch (pri_bus_width) {
+	case 0x00:
+		ret_val = MV_DDR_PRI_BUS_WIDTH_8;
+		break;
+	case 0x01:
+		ret_val = MV_DDR_PRI_BUS_WIDTH_16;
+		break;
+	case 0x02:
+		ret_val = MV_DDR_PRI_BUS_WIDTH_32;
+		break;
+	case 0x03:
+		ret_val = MV_DDR_PRI_BUS_WIDTH_64;
+		break;
+	default:
+		ret_val = MV_DDR_PRI_BUS_WIDTH_LAST;
+	}
+
+	return ret_val;
+}
+
+enum mv_ddr_pkg_rank mv_ddr_spd_bus_width_ext_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char bus_width_ext = spd_data->byte_fields.byte_13.bit_fields.bus_width_ext;
+	enum mv_ddr_bus_width_ext ret_val;
+
+	switch (bus_width_ext) {
+	case 0x00:
+		ret_val = MV_DDR_BUS_WIDTH_EXT_0;
+		break;
+	case 0x01:
+		ret_val = MV_DDR_BUS_WIDTH_EXT_8;
+		break;
+	default:
+		ret_val = MV_DDR_BUS_WIDTH_EXT_LAST;
+	}
+
+	return ret_val;
+}
+
+static enum mv_ddr_pkg_rank mv_ddr_spd_pkg_rank_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char pkg_rank = spd_data->byte_fields.byte_12.bit_fields.dimm_pkg_ranks_num;
+	enum mv_ddr_pkg_rank ret_val;
+
+	switch (pkg_rank) {
+	case 0x00:
+		ret_val = MV_DDR_PKG_RANK_1;
+		break;
+	case 0x01:
+		ret_val = MV_DDR_PKG_RANK_2;
+		break;
+	case 0x02:
+		ret_val = MV_DDR_PKG_RANK_3;
+		break;
+	case 0x03:
+		ret_val = MV_DDR_PKG_RANK_4;
+		break;
+	case 0x04:
+		ret_val = MV_DDR_PKG_RANK_5;
+		break;
+	case 0x05:
+		ret_val = MV_DDR_PKG_RANK_6;
+		break;
+	case 0x06:
+		ret_val = MV_DDR_PKG_RANK_7;
+		break;
+	case 0x07:
+		ret_val = MV_DDR_PKG_RANK_8;
+		break;
+	default:
+		ret_val = MV_DDR_PKG_RANK_LAST;
+	}
+
+	return ret_val;
+}
+
+static enum mv_ddr_die_count mv_ddr_spd_die_count_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char die_count = spd_data->byte_fields.byte_6.bit_fields.die_count;
+	enum mv_ddr_die_count ret_val;
+
+	switch (die_count) {
+	case 0x00:
+		ret_val = MV_DDR_DIE_CNT_1;
+		break;
+	case 0x01:
+		ret_val = MV_DDR_DIE_CNT_2;
+		break;
+	case 0x02:
+		ret_val = MV_DDR_DIE_CNT_3;
+		break;
+	case 0x03:
+		ret_val = MV_DDR_DIE_CNT_4;
+		break;
+	case 0x04:
+		ret_val = MV_DDR_DIE_CNT_5;
+		break;
+	case 0x05:
+		ret_val = MV_DDR_DIE_CNT_6;
+		break;
+	case 0x06:
+		ret_val = MV_DDR_DIE_CNT_7;
+		break;
+	case 0x07:
+		ret_val = MV_DDR_DIE_CNT_8;
+		break;
+	default:
+		ret_val = MV_DDR_DIE_CNT_LAST;
+	}
+
+	return ret_val;
+}
+
+unsigned char mv_ddr_spd_cs_bit_mask_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char cs_bit_mask = 0x0;
+	enum mv_ddr_pkg_rank pkg_rank = mv_ddr_spd_pkg_rank_get(spd_data);
+	enum mv_ddr_die_count die_cnt = mv_ddr_spd_die_count_get(spd_data);
+
+	if (pkg_rank == MV_DDR_PKG_RANK_1 && die_cnt == MV_DDR_DIE_CNT_1)
+		cs_bit_mask = 0x1;
+	else if (pkg_rank == MV_DDR_PKG_RANK_1 && die_cnt == MV_DDR_DIE_CNT_2)
+		cs_bit_mask = 0x3;
+	else if (pkg_rank == MV_DDR_PKG_RANK_2 && die_cnt == MV_DDR_DIE_CNT_1)
+		cs_bit_mask = 0x3;
+	else if (pkg_rank == MV_DDR_PKG_RANK_2 && die_cnt == MV_DDR_DIE_CNT_2)
+		cs_bit_mask = 0xf;
+
+	return cs_bit_mask;
+}
+
+unsigned char mv_ddr_spd_dev_type_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char dev_type = spd_data->byte_fields.byte_2;
+
+	return dev_type;
+}
+
+unsigned char mv_ddr_spd_module_type_get(union mv_ddr_spd_data *spd_data)
+{
+	unsigned char module_type = spd_data->byte_fields.byte_3.bit_fields.module_type;
+
+	return module_type;
+}
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_spd.h b/drivers/ddr/marvell/a38x/mv_ddr_spd.h
new file mode 100644
index 0000000..b4bfef3
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_spd.h
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _MV_DDR_SPD_H
+#define _MV_DDR_SPD_H
+
+#include "mv_ddr_topology.h"
+
+/*
+ * Based on JEDEC Standard No. 21-C, 4.1.2.L-4:
+ * Serial Presence Detect (SPD) for DDR4 SDRAM Modules
+ */
+
+/* block 0: base configuration and dram parameters */
+#define MV_DDR_SPD_DATA_BLOCK0_SIZE		128
+/* block 1: module specific parameters sub-block */
+#define MV_DDR_SPD_DATA_BLOCK1M_SIZE		64
+/* block 1: hybrid memory parameters sub-block */
+#define MV_DDR_SPD_DATA_BLOCK1H_SIZE		64
+/* block 2: extended function parameter block */
+#define MV_DDR_SPD_DATA_BLOCK2E_SIZE		64
+/* block 2: manufacturing information */
+#define MV_DDR_SPD_DATA_BLOCK2M_SIZE		64
+/* block 3: end user programmable */
+#define MV_DDR_SPD_DATA_BLOCK3_SIZE		128
+
+#define MV_DDR_SPD_DEV_TYPE_DDR4		0xc
+#define MV_DDR_SPD_MODULE_TYPE_UDIMM		0x2
+#define MV_DDR_SPD_MODULE_TYPE_SO_DIMM		0x3
+#define MV_DDR_SPD_MODULE_TYPE_MINI_UDIMM	0x6
+#define MV_DDR_SPD_MODULE_TYPE_72BIT_SO_UDIMM	0x9
+#define MV_DDR_SPD_MODULE_TYPE_16BIT_SO_DIMM	0xc
+#define MV_DDR_SPD_MODULE_TYPE_32BIT_SO_DIMM	0xd
+
+/*
+ * TODO: For now, the struct contains block 0 & block 1 with module specific
+ * parameters for unbuffered memory module types only.
+ */
+union mv_ddr_spd_data {
+	unsigned char all_bytes[MV_DDR_SPD_DATA_BLOCK0_SIZE +
+				MV_DDR_SPD_DATA_BLOCK1M_SIZE];
+	struct {
+		/* block 0 */
+		union { /* num of bytes used/num of bytes in spd device/crc coverage */
+			unsigned char all_bits;
+			struct {
+				unsigned char spd_bytes_used:4,
+					spd_bytes_total:3,
+					reserved:1;
+			} bit_fields;
+		} byte_0;
+		union { /* spd revision */
+			unsigned char all_bits;
+			struct {
+				unsigned char addtions_level:4,
+					encoding_level:4;
+			} bit_fields;
+		} byte_1;
+		unsigned char  byte_2; /* key_byte/dram device type */
+		union { /* key byte/module type */
+			unsigned char all_bits;
+			struct {
+				unsigned char module_type:4,
+					hybrid_media:3,
+					hybrid:1;
+			} bit_fields;
+		} byte_3;
+		union { /* sdram density & banks */
+			unsigned char all_bits;
+			struct {
+				unsigned char die_capacity:4,
+					bank_address:2,
+					bank_group:2;
+			} bit_fields;
+		} byte_4;
+		union { /* sdram addressing */
+			unsigned char all_bits;
+			struct {
+				unsigned char col_address:3,
+					row_address:3,
+					reserved:2;
+			} bit_fields;
+		} byte_5;
+		union { /* sdram package type */
+			unsigned char all_bits;
+			struct {
+				unsigned char signal_loading:2,
+					reserved:2,
+					die_count:3,
+					sdram_package_type:1;
+			} bit_fields;
+		} byte_6;
+		union { /* sdram optional features */
+			unsigned char all_bits;
+			struct {
+				unsigned char mac:4, /* max activate count */
+					t_maw:2, /* max activate window */
+					reserved:2; /* all 0s */
+			} bit_fields;
+		} byte_7;
+		unsigned char byte_8; /* sdram thermal & refresh options; reserved; 0x00 */
+		union { /* other sdram optional features */
+			unsigned char all_bits;
+			struct {
+				unsigned char reserved:5, /* all 0s */
+					soft_ppr:1,
+					ppr:2; /* post package repair */
+			} bit_fields;
+		} byte_9;
+		union { /* secondary sdram package type */
+			unsigned char all_bits;
+			struct {
+				unsigned char signal_loading:2,
+					density_ratio:2, /* dram density ratio */
+					die_count:3,
+					sdram_package_type:1;
+			} bit_fields;
+		} byte_10;
+		union { /* module nominal voltage, vdd */
+			unsigned char all_bits;
+			struct {
+				unsigned char operable:1,
+					endurant:1,
+					reserved:5; /* all 0s */
+			} bit_fields;
+		} byte_11;
+		union { /* module organization*/
+			unsigned char all_bits;
+			struct {
+				unsigned char device_width:3,
+					dimm_pkg_ranks_num:3, /* package ranks per dimm number */
+					rank_mix:1,
+					reserved:1; /* 0 */
+			} bit_fields;
+		} byte_12;
+		union { /* module memory bus width */
+			unsigned char all_bits;
+			struct {
+				unsigned char primary_bus_width:3, /* in bits */
+					bus_width_ext:2, /* in bits */
+					reserved:3; /* all 0s */
+			} bit_fields;
+		} byte_13;
+		union { /* module thernal sensor */
+			unsigned char all_bits;
+			struct {
+				unsigned char reserved:7,
+					thermal_sensor:1;
+			} bit_fields;
+		} byte_14;
+		union { /* extended module type */
+			unsigned char all_bits;
+			struct {
+				unsigned char ext_base_module_type:4,
+					reserved:4; /* all 0s */
+			} bit_fields;
+		} byte_15;
+		unsigned char byte_16; /* reserved; 0x00 */
+		union { /* timebases */
+			unsigned char all_bits;
+			struct {
+				unsigned char ftb:2, /* fine timebase */
+					mtb:2, /* medium timebase */
+					reserved:4; /* all 0s */
+			} bit_fields;
+		} byte_17;
+		unsigned char byte_18; /* sdram min cycle time (t ck avg min), mtb */
+		unsigned char byte_19; /* sdram max cycle time (t ck avg max), mtb */
+		unsigned char byte_20; /* cas latencies supported, first byte */
+		unsigned char byte_21; /* cas latencies supported, second byte */
+		unsigned char byte_22; /* cas latencies supported, third byte */
+		unsigned char byte_23; /* cas latencies supported, fourth byte */
+		unsigned char byte_24; /* min cas latency time (t aa min), mtb */
+		unsigned char byte_25; /* min ras to cas delay time (t rcd min), mtb */
+		unsigned char byte_26; /* min row precharge delay time (t rp min), mtb */
+		union { /* upper nibbles for t ras min & t rc min */
+			unsigned char all_bits;
+			struct {
+				unsigned char t_ras_min_msn:4, /* t ras min most significant nibble */
+					t_rc_min_msn:4; /* t rc min most significant nibble */
+			} bit_fields;
+		} byte_27;
+		unsigned char byte_28; /* min active to precharge delay time (t ras min), l-s-byte, mtb */
+		unsigned char byte_29; /* min active to active/refresh delay time (t rc min), l-s-byte, mtb */
+		unsigned char byte_30; /* min refresh recovery delay time (t rfc1 min), l-s-byte, mtb */
+		unsigned char byte_31; /* min refresh recovery delay time (t rfc1 min), m-s-byte, mtb */
+		unsigned char byte_32; /* min refresh recovery delay time (t rfc2 min), l-s-byte, mtb */
+		unsigned char byte_33; /* min refresh recovery delay time (t rfc2 min), m-s-byte, mtb */
+		unsigned char byte_34; /* min refresh recovery delay time (t rfc4 min), l-s-byte, mtb */
+		unsigned char byte_35; /* min refresh recovery delay time (t rfc4 min), m-s-byte, mtb */
+		union { /* upper nibble for t faw */
+			unsigned char all_bits;
+			struct {
+				unsigned char t_faw_min_msn:4, /* t faw min most significant nibble */
+					reserved:4;
+			} bit_fields;
+		} byte_36;
+		unsigned char byte_37; /* min four activate window delay time (t faw min), l-s-byte, mtb */
+		/* byte 38: min activate to activate delay time (t rrd_s min), diff bank group, mtb */
+		unsigned char byte_38;
+		/* byte 39: min activate to activate delay time (t rrd_l min), same bank group, mtb */
+		unsigned char byte_39;
+		unsigned char byte_40; /* min cas to cas delay time (t ccd_l min), same bank group, mtb */
+		union { /* upper nibble for t wr min */
+			unsigned char all_bits;
+			struct {
+				unsigned char t_wr_min_msn:4, /* t wr min most significant nibble */
+					reserved:4;
+			} bit_fields;
+		} byte_41;
+		unsigned char byte_42; /* min write recovery time (t wr min) */
+		union { /* upper nibbles for t wtr min */
+			unsigned char all_bits;
+			struct {
+				unsigned char t_wtr_s_min_msn:4, /* t wtr s min most significant nibble */
+					t_wtr_l_min_msn:4; /* t wtr l min most significant nibble */
+			} bit_fields;
+		} byte_43;
+		unsigned char byte_44; /* min write to read time (t wtr s min), diff bank group, mtb */
+		unsigned char byte_45; /* min write to read time (t wtr l min), same bank group, mtb */
+		unsigned char bytes_46_59[14]; /* reserved; all 0s */
+		unsigned char bytes_60_77[18]; /* TODO: connector to sdram bit mapping */
+		unsigned char bytes_78_116[39]; /* reserved; all 0s */
+		/* fine offset for min cas to cas delay time (t ccd_l min), same bank group, ftb */
+		unsigned char byte_117;
+		/* fine offset for min activate to activate delay time (t rrd_l min), same bank group, ftb */
+		unsigned char byte_118;
+		/* fine offset for min activate to activate delay time (t rrd_s min), diff bank group, ftb */
+		unsigned char byte_119;
+		/* fine offset for min active to active/refresh delay time (t rc min), ftb */
+		unsigned char byte_120;
+		unsigned char byte_121; /* fine offset for min row precharge delay time (t rp min), ftb */
+		unsigned char byte_122; /* fine offset for min ras to cas delay time (t rcd min), ftb */
+		unsigned char byte_123; /* fine offset for min cas latency time (t aa min), ftb */
+		unsigned char byte_124; /* fine offset for sdram max cycle time (t ck avg max), ftb */
+		unsigned char byte_125; /* fine offset for sdram min cycle time (t ck avg min), ftb */
+		unsigned char byte_126; /* crc for base configuration section, l-s-byte */
+		unsigned char byte_127; /* crc for base configuration section, m-s-byte */
+		/*
+		 * block 1: module specific parameters for unbuffered memory module types only
+		 */
+		union { /* (unbuffered) raw card extension, module nominal height */
+			unsigned char all_bits;
+			struct {
+				unsigned char nom_height_max:5, /* in mm */
+					raw_cad_ext:3;
+			} bit_fields;
+		} byte_128;
+		union { /* (unbuffered) module maximum thickness */
+			unsigned char all_bits;
+			struct {
+				unsigned char front_thickness_max:4, /* in mm */
+					back_thickness_max:4; /* in mm */
+			} bit_fields;
+		} byte_129;
+		union { /* (unbuffered) reference raw card used */
+			unsigned char all_bits;
+			struct {
+				unsigned char ref_raw_card:5,
+					ref_raw_card_rev:2,
+					ref_raw_card_ext:1;
+			} bit_fields;
+		} byte_130;
+		union { /* (unbuffered) address mapping from edge connector to dram */
+			unsigned char all_bits;
+			struct {
+				unsigned char rank_1_mapping:1,
+					reserved:7;
+			} bit_fields;
+		} byte_131;
+		unsigned char bytes_132_191[60]; /* reserved; all 0s */
+	} byte_fields;
+};
+
+int mv_ddr_spd_timing_calc(union mv_ddr_spd_data *spd_data, unsigned int timing_data[]);
+enum mv_ddr_dev_width mv_ddr_spd_dev_width_get(union mv_ddr_spd_data *spd_data);
+enum mv_ddr_die_capacity mv_ddr_spd_die_capacity_get(union mv_ddr_spd_data *spd_data);
+unsigned char mv_ddr_spd_mem_mirror_get(union mv_ddr_spd_data *spd_data);
+unsigned char mv_ddr_spd_cs_bit_mask_get(union mv_ddr_spd_data *spd_data);
+unsigned char mv_ddr_spd_dev_type_get(union mv_ddr_spd_data *spd_data);
+unsigned char mv_ddr_spd_module_type_get(union mv_ddr_spd_data *spd_data);
+int mv_ddr_spd_supported_cls_calc(union mv_ddr_spd_data *spd_data);
+unsigned int mv_ddr_spd_supported_cl_get(unsigned int cl);
+enum mv_ddr_pkg_rank mv_ddr_spd_pri_bus_width_get(union mv_ddr_spd_data *spd_data);
+enum mv_ddr_pkg_rank mv_ddr_spd_bus_width_ext_get(union mv_ddr_spd_data *spd_data);
+
+#endif /* _MV_DDR_SPD_H */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_sys_env_lib.c b/drivers/ddr/marvell/a38x/mv_ddr_sys_env_lib.c
new file mode 100644
index 0000000..c9c6899
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_sys_env_lib.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "mv_ddr_regs.h"
+#include "mv_ddr_sys_env_lib.h"
+
+static u32 mv_ddr_board_id_get(void)
+{
+#if defined(CONFIG_TARGET_DB_88F6820_GP)
+	return DB_GP_68XX_ID;
+#else
+	/*
+	 * Return 0 here for custom board as this should not be used
+	 * for custom boards.
+	 */
+	return 0;
+#endif
+}
+
+static u32 mv_ddr_board_id_index_get(u32 board_id)
+{
+	/*
+	 * Marvell Boards use 0x10 as base for Board ID:
+	 * mask MSB to receive index for board ID
+	 */
+	return board_id & (MARVELL_BOARD_ID_MASK - 1);
+}
+
+/*
+ * read gpio input for suspend-wakeup indication
+ * return indicating suspend wakeup status:
+ * 0 - not supported,
+ * 1 - supported: read magic word detect wakeup,
+ * 2 - detected wakeup from gpio
+ */
+enum suspend_wakeup_status mv_ddr_sys_env_suspend_wakeup_check(void)
+{
+	u32 reg, board_id_index, gpio;
+	struct board_wakeup_gpio board_gpio[] = MV_BOARD_WAKEUP_GPIO_INFO;
+
+	board_id_index = mv_ddr_board_id_index_get(mv_ddr_board_id_get());
+	if (!(sizeof(board_gpio) / sizeof(struct board_wakeup_gpio) >
+	      board_id_index)) {
+		printf("\n_failed loading Suspend-Wakeup information (invalid board ID)\n");
+		return SUSPEND_WAKEUP_DISABLED;
+	}
+
+	/*
+	 * - Detect if Suspend-Wakeup is supported on current board
+	 * - Fetch the GPIO number for wakeup status input indication
+	 */
+	if (board_gpio[board_id_index].gpio_num == -1) {
+		/* Suspend to RAM is not supported */
+		return SUSPEND_WAKEUP_DISABLED;
+	} else if (board_gpio[board_id_index].gpio_num == -2) {
+		/*
+		 * Suspend to RAM is supported but GPIO indication is
+		 * not implemented - Skip
+		 */
+		return SUSPEND_WAKEUP_ENABLED;
+	} else {
+		gpio = board_gpio[board_id_index].gpio_num;
+	}
+
+	/* Initialize MPP for GPIO (set MPP = 0x0) */
+	reg = reg_read(MPP_CONTROL_REG(MPP_REG_NUM(gpio)));
+	/* reset MPP21 to 0x0, keep rest of MPP settings*/
+	reg &= ~MPP_MASK(gpio);
+	reg_write(MPP_CONTROL_REG(MPP_REG_NUM(gpio)), reg);
+
+	/* Initialize GPIO as input */
+	reg = reg_read(GPP_DATA_OUT_EN_REG(GPP_REG_NUM(gpio)));
+	reg |= GPP_MASK(gpio);
+	reg_write(GPP_DATA_OUT_EN_REG(GPP_REG_NUM(gpio)), reg);
+
+	/*
+	 * Check GPP for input status from PIC: 0 - regular init,
+	 * 1 - suspend wakeup
+	 */
+	reg = reg_read(GPP_DATA_IN_REG(GPP_REG_NUM(gpio)));
+
+	/* if GPIO is ON: wakeup from S2RAM indication detected */
+	return (reg & GPP_MASK(gpio)) ? SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED :
+		SUSPEND_WAKEUP_DISABLED;
+}
+
+/*
+ * get bit mask of enabled cs
+ * return bit mask of enabled cs:
+ * 1 - only cs0 enabled,
+ * 3 - both cs0 and cs1 enabled
+ */
+u32 mv_ddr_sys_env_get_cs_ena_from_reg(void)
+{
+	return reg_read(DDR3_RANK_CTRL_REG) &
+		((CS_EXIST_MASK << CS_EXIST_OFFS(0)) |
+		 (CS_EXIST_MASK << CS_EXIST_OFFS(1)) |
+		 (CS_EXIST_MASK << CS_EXIST_OFFS(2)) |
+		 (CS_EXIST_MASK << CS_EXIST_OFFS(3)));
+}
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_sys_env_lib.h b/drivers/ddr/marvell/a38x/mv_ddr_sys_env_lib.h
new file mode 100644
index 0000000..dc6977c
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_sys_env_lib.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _MV_DDR_SYS_ENV_LIB_H
+#define _MV_DDR_SYS_ENV_LIB_H
+
+#include "ddr_ml_wrapper.h"
+
+/* device revision */
+#define DEV_ID_REG			0x18238
+#define DEV_VERSION_ID_REG		0x1823c
+#define REVISON_ID_OFFS			8
+#define REVISON_ID_MASK			0xf00
+
+#define MPP_CONTROL_REG(id)		(0x18000 + (id * 4))
+#define GPP_DATA_OUT_REG(grp)		(MV_GPP_REGS_BASE(grp) + 0x00)
+#define GPP_DATA_OUT_EN_REG(grp)	(MV_GPP_REGS_BASE(grp) + 0x04)
+#define GPP_DATA_IN_REG(grp)		(MV_GPP_REGS_BASE(grp) + 0x10)
+#define MV_GPP_REGS_BASE(unit)		(0x18100 + ((unit) * 0x40))
+
+#define MPP_REG_NUM(GPIO_NUM)		(GPIO_NUM / 8)
+#define MPP_MASK(GPIO_NUM)		(0xf << 4 * (GPIO_NUM - \
+					(MPP_REG_NUM(GPIO_NUM) * 8)));
+#define GPP_REG_NUM(GPIO_NUM)		(GPIO_NUM / 32)
+#define GPP_MASK(GPIO_NUM)		(1 << GPIO_NUM % 32)
+
+/* device ID */
+/* Board ID numbers */
+#define MARVELL_BOARD_ID_MASK		0x10
+
+/* Customer boards for A38x */
+#define A38X_CUSTOMER_BOARD_ID_BASE	0x0
+#define A38X_CUSTOMER_BOARD_ID0		(A38X_CUSTOMER_BOARD_ID_BASE + 0)
+#define A38X_CUSTOMER_BOARD_ID1		(A38X_CUSTOMER_BOARD_ID_BASE + 1)
+#define A38X_MV_MAX_CUSTOMER_BOARD_ID	(A38X_CUSTOMER_BOARD_ID_BASE + 2)
+#define A38X_MV_CUSTOMER_BOARD_NUM	(A38X_MV_MAX_CUSTOMER_BOARD_ID - \
+					 A38X_CUSTOMER_BOARD_ID_BASE)
+
+/* Marvell boards for A38x */
+#define A38X_MARVELL_BOARD_ID_BASE	0x10
+#define RD_NAS_68XX_ID			(A38X_MARVELL_BOARD_ID_BASE + 0)
+#define DB_68XX_ID			(A38X_MARVELL_BOARD_ID_BASE + 1)
+#define RD_AP_68XX_ID			(A38X_MARVELL_BOARD_ID_BASE + 2)
+#define DB_AP_68XX_ID			(A38X_MARVELL_BOARD_ID_BASE + 3)
+#define DB_GP_68XX_ID			(A38X_MARVELL_BOARD_ID_BASE + 4)
+#define DB_BP_6821_ID			(A38X_MARVELL_BOARD_ID_BASE + 5)
+#define DB_AMC_6820_ID			(A38X_MARVELL_BOARD_ID_BASE + 6)
+#define A38X_MV_MAX_MARVELL_BOARD_ID	(A38X_MARVELL_BOARD_ID_BASE + 7)
+#define A38X_MV_MARVELL_BOARD_NUM	(A38X_MV_MAX_MARVELL_BOARD_ID - \
+					 A38X_MARVELL_BOARD_ID_BASE)
+
+/* Marvell boards for A39x */
+#define A39X_MARVELL_BOARD_ID_BASE	0x30
+#define A39X_DB_69XX_ID			(A39X_MARVELL_BOARD_ID_BASE + 0)
+#define A39X_RD_69XX_ID			(A39X_MARVELL_BOARD_ID_BASE + 1)
+#define A39X_MV_MAX_MARVELL_BOARD_ID	(A39X_MARVELL_BOARD_ID_BASE + 2)
+#define A39X_MV_MARVELL_BOARD_NUM	(A39X_MV_MAX_MARVELL_BOARD_ID - \
+					 A39X_MARVELL_BOARD_ID_BASE)
+
+struct board_wakeup_gpio {
+	u32 board_id;
+	int gpio_num;
+};
+
+enum suspend_wakeup_status {
+	SUSPEND_WAKEUP_DISABLED,
+	SUSPEND_WAKEUP_ENABLED,
+	SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED,
+};
+
+/*
+ * GPIO status indication for Suspend Wakeup:
+ * If suspend to RAM is supported and GPIO inidcation is implemented,
+ * set the gpio number
+ * If suspend to RAM is supported but GPIO indication is not implemented
+ * set '-2'
+ * If suspend to RAM is not supported set '-1'
+ */
+#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT
+#ifdef CONFIG_ARMADA_38X
+#define MV_BOARD_WAKEUP_GPIO_INFO {		\
+	{A38X_CUSTOMER_BOARD_ID0,	-1 },	\
+	{A38X_CUSTOMER_BOARD_ID0,	-1 },	\
+};
+#else
+#define MV_BOARD_WAKEUP_GPIO_INFO {		\
+	{A39X_CUSTOMER_BOARD_ID0,	-1 },	\
+	{A39X_CUSTOMER_BOARD_ID0,	-1 },	\
+};
+#endif /* CONFIG_ARMADA_38X */
+
+#else
+
+#ifdef CONFIG_ARMADA_38X
+#define MV_BOARD_WAKEUP_GPIO_INFO {	\
+	{RD_NAS_68XX_ID, -2 },		\
+	{DB_68XX_ID,	 -1 },		\
+	{RD_AP_68XX_ID,	 -2 },		\
+	{DB_AP_68XX_ID,	 -2 },		\
+	{DB_GP_68XX_ID,	 -2 },		\
+	{DB_BP_6821_ID,	 -2 },		\
+	{DB_AMC_6820_ID, -2 },		\
+};
+#else
+#define MV_BOARD_WAKEUP_GPIO_INFO {	\
+	{A39X_RD_69XX_ID, -1 },		\
+	{A39X_DB_69XX_ID, -1 },		\
+};
+#endif /* CONFIG_ARMADA_38X */
+#endif /* CONFIG_CUSTOMER_BOARD_SUPPORT */
+
+enum suspend_wakeup_status mv_ddr_sys_env_suspend_wakeup_check(void);
+u32 mv_ddr_sys_env_get_cs_ena_from_reg(void);
+
+#endif /* _MV_DDR_SYS_ENV_LIB_H */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_topology.c b/drivers/ddr/marvell/a38x/mv_ddr_topology.c
new file mode 100644
index 0000000..ef3b658
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_topology.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+#include "ddr_ml_wrapper.h"
+#include "mv_ddr_plat.h"
+
+#include "mv_ddr_topology.h"
+#include "mv_ddr_common.h"
+#include "mv_ddr_spd.h"
+#include "ddr_topology_def.h"
+#include "ddr3_training_ip_db.h"
+#include "ddr3_training_ip.h"
+#include "mv_ddr_training_db.h"
+
+unsigned int mv_ddr_cl_calc(unsigned int taa_min, unsigned int tclk)
+{
+	unsigned int cl = ceil_div(taa_min, tclk);
+
+	return mv_ddr_spd_supported_cl_get(cl);
+
+}
+
+unsigned int mv_ddr_cwl_calc(unsigned int tclk)
+{
+	unsigned int cwl;
+
+	if (tclk >= 1250)
+		cwl = 9;
+	else if (tclk >= 1071)
+		cwl = 10;
+	else if (tclk >= 938)
+		cwl = 11;
+	else if (tclk >= 833)
+		cwl = 12;
+	else if (tclk >= 750)
+		cwl = 14;
+	else if (tclk >= 625)
+		cwl = 16;
+	else
+		cwl = 0;
+
+	return cwl;
+}
+
+int mv_ddr_topology_map_update(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	struct if_params *iface_params = &(tm->interface_params[0]);
+	unsigned int octets_per_if_num = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+	enum mv_ddr_speed_bin speed_bin_index;
+	enum mv_ddr_freq freq = MV_DDR_FREQ_LAST;
+	unsigned int tclk;
+	unsigned char val = 0;
+	int i;
+
+	if (iface_params->memory_freq == MV_DDR_FREQ_SAR)
+		iface_params->memory_freq = mv_ddr_init_freq_get();
+
+	if (tm->cfg_src == MV_DDR_CFG_SPD) {
+		/* check dram device type */
+		val = mv_ddr_spd_dev_type_get(&tm->spd_data);
+		if (val != MV_DDR_SPD_DEV_TYPE_DDR4) {
+			printf("mv_ddr: unsupported dram device type found\n");
+			return -1;
+		}
+
+		/* update topology map with timing data */
+		if (mv_ddr_spd_timing_calc(&tm->spd_data, tm->timing_data) > 0) {
+			printf("mv_ddr: negative timing data found\n");
+			return -1;
+		}
+
+		/* update device width in topology map */
+		iface_params->bus_width = mv_ddr_spd_dev_width_get(&tm->spd_data);
+
+		/* update die capacity in topology map */
+		iface_params->memory_size = mv_ddr_spd_die_capacity_get(&tm->spd_data);
+
+		/* update bus bit mask in topology map */
+		tm->bus_act_mask = mv_ddr_bus_bit_mask_get();
+
+		/* update cs bit mask in topology map */
+		val = mv_ddr_spd_cs_bit_mask_get(&tm->spd_data);
+		for (i = 0; i < octets_per_if_num; i++)
+			iface_params->as_bus_params[i].cs_bitmask = val;
+
+		/* check dram module type */
+		val = mv_ddr_spd_module_type_get(&tm->spd_data);
+		switch (val) {
+		case MV_DDR_SPD_MODULE_TYPE_UDIMM:
+		case MV_DDR_SPD_MODULE_TYPE_SO_DIMM:
+		case MV_DDR_SPD_MODULE_TYPE_MINI_UDIMM:
+		case MV_DDR_SPD_MODULE_TYPE_72BIT_SO_UDIMM:
+		case MV_DDR_SPD_MODULE_TYPE_16BIT_SO_DIMM:
+		case MV_DDR_SPD_MODULE_TYPE_32BIT_SO_DIMM:
+			break;
+		default:
+			printf("mv_ddr: unsupported dram module type found\n");
+			return -1;
+		}
+
+		/* update mirror bit mask in topology map */
+		val = mv_ddr_spd_mem_mirror_get(&tm->spd_data);
+		for (i = 0; i < octets_per_if_num; i++)
+			iface_params->as_bus_params[i].mirror_enable_bitmask = val << 1;
+
+		tclk = 1000000 / mv_ddr_freq_get(iface_params->memory_freq);
+		/* update cas write latency (cwl) */
+		val = mv_ddr_cwl_calc(tclk);
+		if (val == 0) {
+			printf("mv_ddr: unsupported cas write latency value found\n");
+			return -1;
+		}
+		iface_params->cas_wl = val;
+
+		/* update cas latency (cl) */
+		mv_ddr_spd_supported_cls_calc(&tm->spd_data);
+		val = mv_ddr_cl_calc(tm->timing_data[MV_DDR_TAA_MIN], tclk);
+		if (val == 0) {
+			printf("mv_ddr: unsupported cas latency value found\n");
+			return -1;
+		}
+		iface_params->cas_l = val;
+	} else if (tm->cfg_src == MV_DDR_CFG_DEFAULT) {
+		/* set cas and cas-write latencies per speed bin, if they unset */
+		speed_bin_index = iface_params->speed_bin_index;
+		freq = iface_params->memory_freq;
+
+		if (iface_params->cas_l == 0)
+			iface_params->cas_l = mv_ddr_cl_val_get(speed_bin_index, freq);
+
+		if (iface_params->cas_wl == 0)
+			iface_params->cas_wl = mv_ddr_cwl_val_get(speed_bin_index, freq);
+	}
+
+	return 0;
+}
+
+unsigned short mv_ddr_bus_bit_mask_get(void)
+{
+	unsigned short pri_and_ext_bus_width = 0x0;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int octets_per_if_num = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+
+	if (tm->cfg_src == MV_DDR_CFG_SPD) {
+		enum mv_ddr_pri_bus_width pri_bus_width = mv_ddr_spd_pri_bus_width_get(&tm->spd_data);
+		enum mv_ddr_bus_width_ext bus_width_ext = mv_ddr_spd_bus_width_ext_get(&tm->spd_data);
+
+		switch (pri_bus_width) {
+		case MV_DDR_PRI_BUS_WIDTH_16:
+			pri_and_ext_bus_width = BUS_MASK_16BIT;
+			break;
+		case MV_DDR_PRI_BUS_WIDTH_32:
+			pri_and_ext_bus_width = BUS_MASK_32BIT;
+			break;
+		case MV_DDR_PRI_BUS_WIDTH_64:
+			pri_and_ext_bus_width = MV_DDR_64BIT_BUS_MASK;
+			break;
+		default:
+			pri_and_ext_bus_width = 0x0;
+		}
+
+		if (bus_width_ext == MV_DDR_BUS_WIDTH_EXT_8)
+			pri_and_ext_bus_width |= 1 << (octets_per_if_num - 1);
+	}
+
+	return pri_and_ext_bus_width;
+}
+
+unsigned int mv_ddr_if_bus_width_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int bus_width;
+
+	switch (tm->bus_act_mask) {
+	case BUS_MASK_16BIT:
+	case BUS_MASK_16BIT_ECC:
+	case BUS_MASK_16BIT_ECC_PUP3:
+		bus_width = 16;
+		break;
+	case BUS_MASK_32BIT:
+	case BUS_MASK_32BIT_ECC:
+	case MV_DDR_32BIT_ECC_PUP8_BUS_MASK:
+		bus_width = 32;
+		break;
+	case MV_DDR_64BIT_BUS_MASK:
+	case MV_DDR_64BIT_ECC_PUP8_BUS_MASK:
+		bus_width = 64;
+		break;
+	default:
+		printf("mv_ddr: unsupported bus active mask parameter found\n");
+		bus_width = 0;
+	}
+
+	return bus_width;
+}
+
+unsigned int mv_ddr_cs_num_get(void)
+{
+	unsigned int cs_num = 0;
+	unsigned int cs, sphy;
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	struct if_params *iface_params = &(tm->interface_params[0]);
+	unsigned int sphy_max = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+
+	for (sphy = 0; sphy < sphy_max; sphy++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, sphy);
+		break;
+	}
+
+	for (cs = 0; cs < MAX_CS_NUM; cs++) {
+		VALIDATE_ACTIVE(iface_params->as_bus_params[sphy].cs_bitmask, cs);
+		cs_num++;
+	}
+
+	return cs_num;
+}
+
+int mv_ddr_is_ecc_ena(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask) ||
+	    DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask) ||
+	    DDR3_IS_ECC_PUP8_MODE(tm->bus_act_mask))
+		return 1;
+	else
+		return 0;
+}
+
+/* translate topology map definition to real memory size in bits */
+static unsigned int mem_size[] = {
+	ADDR_SIZE_512MB,
+	ADDR_SIZE_1GB,
+	ADDR_SIZE_2GB,
+	ADDR_SIZE_4GB,
+	ADDR_SIZE_8GB
+	/* TODO: add capacity up to 256GB */
+};
+
+unsigned long long mv_ddr_mem_sz_per_cs_get(void)
+{
+	unsigned long long mem_sz_per_cs;
+	unsigned int i, sphys, sphys_per_dunit;
+	unsigned int sphy_max = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	struct if_params *iface_params = &(tm->interface_params[0]);
+
+	/* calc number of active subphys excl. ecc one */
+	for (i = 0, sphys = 0; i < sphy_max - 1; i++) {
+		VALIDATE_BUS_ACTIVE(tm->bus_act_mask, i);
+		sphys++;
+	}
+
+	/* calc number of subphys per ddr unit */
+	if (iface_params->bus_width == MV_DDR_DEV_WIDTH_8BIT)
+		sphys_per_dunit = MV_DDR_ONE_SPHY_PER_DUNIT;
+	else if (iface_params->bus_width == MV_DDR_DEV_WIDTH_16BIT)
+		sphys_per_dunit = MV_DDR_TWO_SPHY_PER_DUNIT;
+	else {
+		printf("mv_ddr: unsupported bus width type found\n");
+		return 0;
+	}
+
+	/* calc dram size per cs */
+	mem_sz_per_cs = (unsigned long long)mem_size[iface_params->memory_size] *
+			(unsigned long long)sphys /
+			(unsigned long long)sphys_per_dunit;
+
+	return mem_sz_per_cs;
+}
+
+unsigned long long mv_ddr_mem_sz_get(void)
+{
+	unsigned long long tot_mem_sz = 0;
+	unsigned long long mem_sz_per_cs = 0;
+	unsigned long long max_cs = mv_ddr_cs_num_get();
+
+	mem_sz_per_cs = mv_ddr_mem_sz_per_cs_get();
+	tot_mem_sz = max_cs * mem_sz_per_cs;
+
+	return tot_mem_sz;
+}
+
+unsigned int mv_ddr_rtt_nom_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int rtt_nom = tm->edata.mem_edata.rtt_nom;
+
+	if (rtt_nom >= MV_DDR_RTT_NOM_PARK_RZQ_LAST) {
+		printf("error: %s: unsupported rtt_nom parameter found\n", __func__);
+		rtt_nom = PARAM_UNDEFINED;
+	}
+
+	return rtt_nom;
+}
+
+unsigned int mv_ddr_rtt_park_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int cs_num = mv_ddr_cs_num_get();
+	unsigned int rtt_park = MV_DDR_RTT_NOM_PARK_RZQ_LAST;
+
+	if (cs_num > 0 && cs_num <= MAX_CS_NUM)
+		rtt_park = tm->edata.mem_edata.rtt_park[cs_num - 1];
+
+	if (rtt_park >= MV_DDR_RTT_NOM_PARK_RZQ_LAST) {
+		printf("error: %s: unsupported rtt_park parameter found\n", __func__);
+		rtt_park = PARAM_UNDEFINED;
+	}
+
+	return rtt_park;
+}
+
+unsigned int mv_ddr_rtt_wr_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int cs_num = mv_ddr_cs_num_get();
+	unsigned int rtt_wr = MV_DDR_RTT_WR_RZQ_LAST;
+
+	if (cs_num > 0 && cs_num <= MAX_CS_NUM)
+		rtt_wr = tm->edata.mem_edata.rtt_wr[cs_num - 1];
+
+	if (rtt_wr >= MV_DDR_RTT_WR_RZQ_LAST) {
+		printf("error: %s: unsupported rtt_wr parameter found\n", __func__);
+		rtt_wr = PARAM_UNDEFINED;
+	}
+
+	return rtt_wr;
+}
+
+unsigned int mv_ddr_dic_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+	unsigned int dic = tm->edata.mem_edata.dic;
+
+	if (dic >= MV_DDR_DIC_RZQ_LAST) {
+		printf("error: %s: unsupported dic parameter found\n", __func__);
+		dic = PARAM_UNDEFINED;
+	}
+
+	return dic;
+}
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_topology.h b/drivers/ddr/marvell/a38x/mv_ddr_topology.h
new file mode 100644
index 0000000..766f25d
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_topology.h
@@ -0,0 +1,329 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _MV_DDR_TOPOLOGY_H
+#define _MV_DDR_TOPOLOGY_H
+
+#define MAX_CS_NUM	4
+
+enum mv_ddr_speed_bin {
+	SPEED_BIN_DDR_800D,
+	SPEED_BIN_DDR_800E,
+	SPEED_BIN_DDR_1066E,
+	SPEED_BIN_DDR_1066F,
+	SPEED_BIN_DDR_1066G,
+	SPEED_BIN_DDR_1333F,
+	SPEED_BIN_DDR_1333G,
+	SPEED_BIN_DDR_1333H,
+	SPEED_BIN_DDR_1333J,
+	SPEED_BIN_DDR_1600G,
+	SPEED_BIN_DDR_1600H,
+	SPEED_BIN_DDR_1600J,
+	SPEED_BIN_DDR_1600K,
+	SPEED_BIN_DDR_1866J,
+	SPEED_BIN_DDR_1866K,
+	SPEED_BIN_DDR_1866L,
+	SPEED_BIN_DDR_1866M,
+	SPEED_BIN_DDR_2133K,
+	SPEED_BIN_DDR_2133L,
+	SPEED_BIN_DDR_2133M,
+	SPEED_BIN_DDR_2133N,
+
+	SPEED_BIN_DDR_1333H_EXT,
+	SPEED_BIN_DDR_1600K_EXT,
+	SPEED_BIN_DDR_1866M_EXT
+};
+
+enum mv_ddr_freq {
+	MV_DDR_FREQ_LOW_FREQ,
+	MV_DDR_FREQ_400,
+	MV_DDR_FREQ_533,
+	MV_DDR_FREQ_667,
+	MV_DDR_FREQ_800,
+	MV_DDR_FREQ_933,
+	MV_DDR_FREQ_1066,
+	MV_DDR_FREQ_311,
+	MV_DDR_FREQ_333,
+	MV_DDR_FREQ_467,
+	MV_DDR_FREQ_850,
+	MV_DDR_FREQ_600,
+	MV_DDR_FREQ_300,
+	MV_DDR_FREQ_900,
+	MV_DDR_FREQ_360,
+	MV_DDR_FREQ_1000,
+	MV_DDR_FREQ_LAST,
+	MV_DDR_FREQ_SAR
+};
+
+enum mv_ddr_speed_bin_timing {
+	SPEED_BIN_TRCD,
+	SPEED_BIN_TRP,
+	SPEED_BIN_TRAS,
+	SPEED_BIN_TRC,
+	SPEED_BIN_TRRD1K,
+	SPEED_BIN_TRRD2K,
+	SPEED_BIN_TPD,
+	SPEED_BIN_TFAW1K,
+	SPEED_BIN_TFAW2K,
+	SPEED_BIN_TWTR,
+	SPEED_BIN_TRTP,
+	SPEED_BIN_TWR,
+	SPEED_BIN_TMOD,
+	SPEED_BIN_TXPDLL,
+	SPEED_BIN_TXSDLL
+};
+
+/* ddr bus masks */
+#define BUS_MASK_32BIT			0xf
+#define BUS_MASK_32BIT_ECC		0x1f
+#define BUS_MASK_16BIT			0x3
+#define BUS_MASK_16BIT_ECC		0x13
+#define BUS_MASK_16BIT_ECC_PUP3		0xb
+#define MV_DDR_64BIT_BUS_MASK		0xff
+#define MV_DDR_64BIT_ECC_PUP8_BUS_MASK	0x1ff
+#define MV_DDR_32BIT_ECC_PUP8_BUS_MASK	0x10f
+
+#define MV_DDR_CS_BITMASK_1CS		0x1
+#define MV_DDR_CS_BITMASK_2CS		0x3
+
+#define MV_DDR_ONE_SPHY_PER_DUNIT	1
+#define MV_DDR_TWO_SPHY_PER_DUNIT	2
+
+/* source of ddr configuration data */
+enum mv_ddr_cfg_src {
+	MV_DDR_CFG_DEFAULT,	/* based on data in mv_ddr_topology_map structure */
+	MV_DDR_CFG_SPD,		/* based on data in spd */
+	MV_DDR_CFG_USER,	/* based on data from user */
+	MV_DDR_CFG_STATIC,	/* based on data from user in register-value format */
+	MV_DDR_CFG_LAST
+};
+
+enum mv_ddr_temperature {
+	MV_DDR_TEMP_LOW,
+	MV_DDR_TEMP_NORMAL,
+	MV_DDR_TEMP_HIGH
+};
+
+enum mv_ddr_timing {
+	MV_DDR_TIM_DEFAULT,
+	MV_DDR_TIM_1T,
+	MV_DDR_TIM_2T
+};
+
+enum mv_ddr_timing_data {
+	MV_DDR_TCK_AVG_MIN, /* sdram min cycle time (t ck avg min) */
+	MV_DDR_TAA_MIN, /* min cas latency time (t aa min) */
+	MV_DDR_TRFC1_MIN, /* min refresh recovery delay time (t rfc1 min) */
+	MV_DDR_TWR_MIN, /* min write recovery time (t wr min) */
+	MV_DDR_TRCD_MIN, /* min ras to cas delay time (t rcd min) */
+	MV_DDR_TRP_MIN, /* min row precharge delay time (t rp min) */
+	MV_DDR_TRC_MIN, /* min active to active/refresh delay time (t rc min) */
+	MV_DDR_TRAS_MIN, /* min active to precharge delay time (t ras min) */
+	MV_DDR_TRRD_S_MIN, /* min activate to activate delay time (t rrd_s min), diff bank group */
+	MV_DDR_TRRD_L_MIN, /* min activate to activate delay time (t rrd_l min), same bank group */
+	MV_DDR_TCCD_L_MIN, /* min cas to cas delay time (t ccd_l min), same bank group */
+	MV_DDR_TFAW_MIN, /* min four activate window delay time (t faw min) */
+	MV_DDR_TWTR_S_MIN, /* min write to read time (t wtr s min), diff bank group */
+	MV_DDR_TWTR_L_MIN, /* min write to read time (t wtr l min), same bank group */
+	MV_DDR_TDATA_LAST
+};
+
+enum mv_ddr_electrical_data {
+	MV_DDR_CK_DLY,
+	MV_DDR_PHY_REG3,
+	MV_DDR_ZPRI_DATA,
+	MV_DDR_ZNRI_DATA,
+	MV_DDR_ZPRI_CTRL,
+	MV_DDR_ZNRI_CTRL,
+	MV_DDR_ZPODT_DATA,
+	MV_DDR_ZNODT_DATA,
+	MV_DDR_ZPODT_CTRL,
+	MV_DDR_ZNODT_CTRL,
+	MV_DDR_DIC,
+	MV_DDR_ODT_CFG,
+	MV_DDR_RTT_NOM,
+	MV_DDR_RTT_WR,
+	MV_DDR_RTT_PARK,
+	MV_DDR_EDATA_LAST
+};
+
+/* memory electrical configuration values */
+enum mv_ddr_rtt_nom_park_evalue {
+	MV_DDR_RTT_NOM_PARK_RZQ_DISABLE,
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV4,	/* 60-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV2,	/* 120-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV6,	/* 40-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV1,	/* 240-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV5,	/* 48-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV3,	/* 80-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_DIV7,	/* 34-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_NOM_PARK_RZQ_LAST
+};
+
+enum mv_ddr_rtt_wr_evalue {
+	MV_DDR_RTT_WR_DYN_ODT_OFF,
+	MV_DDR_RTT_WR_RZQ_DIV2,	/* 120-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_WR_RZQ_DIV1,	/* 240-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_WR_HIZ,
+	MV_DDR_RTT_WR_RZQ_DIV3,	/* 80-Ohm; RZQ = 240-Ohm */
+	MV_DDR_RTT_WR_RZQ_LAST
+};
+
+enum mv_ddr_dic_evalue {
+	MV_DDR_DIC_RZQ_DIV7,	/* 34-Ohm; RZQ = 240-Ohm */
+	MV_DDR_DIC_RZQ_DIV5,	/* 48-Ohm; RZQ = 240-Ohm */
+	MV_DDR_DIC_RZQ_LAST
+};
+
+/* phy electrical configuration values */
+enum mv_ddr_ohm_evalue {
+	MV_DDR_OHM_30 = 30,
+	MV_DDR_OHM_48 = 48,
+	MV_DDR_OHM_60 = 60,
+	MV_DDR_OHM_80 = 80,
+	MV_DDR_OHM_120 = 120,
+	MV_DDR_OHM_240 = 240,
+	MV_DDR_OHM_LAST
+};
+
+/* mac electrical configuration values */
+enum mv_ddr_odt_cfg_evalue {
+	MV_DDR_ODT_CFG_NORMAL,
+	MV_DDR_ODT_CFG_ALWAYS_ON,
+	MV_DDR_ODT_CFG_LAST
+};
+
+enum mv_ddr_dev_width { /* sdram device width */
+	MV_DDR_DEV_WIDTH_4BIT,
+	MV_DDR_DEV_WIDTH_8BIT,
+	MV_DDR_DEV_WIDTH_16BIT,
+	MV_DDR_DEV_WIDTH_32BIT,
+	MV_DDR_DEV_WIDTH_LAST
+};
+
+enum mv_ddr_die_capacity { /* total sdram capacity per die, megabits */
+	MV_DDR_DIE_CAP_256MBIT,
+	MV_DDR_DIE_CAP_512MBIT = 0,
+	MV_DDR_DIE_CAP_1GBIT,
+	MV_DDR_DIE_CAP_2GBIT,
+	MV_DDR_DIE_CAP_4GBIT,
+	MV_DDR_DIE_CAP_8GBIT,
+	MV_DDR_DIE_CAP_16GBIT,
+	MV_DDR_DIE_CAP_32GBIT,
+	MV_DDR_DIE_CAP_12GBIT,
+	MV_DDR_DIE_CAP_24GBIT,
+	MV_DDR_DIE_CAP_LAST
+};
+
+enum mv_ddr_pkg_rank { /* number of package ranks per dimm */
+	MV_DDR_PKG_RANK_1,
+	MV_DDR_PKG_RANK_2,
+	MV_DDR_PKG_RANK_3,
+	MV_DDR_PKG_RANK_4,
+	MV_DDR_PKG_RANK_5,
+	MV_DDR_PKG_RANK_6,
+	MV_DDR_PKG_RANK_7,
+	MV_DDR_PKG_RANK_8,
+	MV_DDR_PKG_RANK_LAST
+};
+
+enum mv_ddr_pri_bus_width { /* number of primary bus width bits */
+	MV_DDR_PRI_BUS_WIDTH_8,
+	MV_DDR_PRI_BUS_WIDTH_16,
+	MV_DDR_PRI_BUS_WIDTH_32,
+	MV_DDR_PRI_BUS_WIDTH_64,
+	MV_DDR_PRI_BUS_WIDTH_LAST
+};
+
+enum mv_ddr_bus_width_ext { /* number of extension bus width bits */
+	MV_DDR_BUS_WIDTH_EXT_0,
+	MV_DDR_BUS_WIDTH_EXT_8,
+	MV_DDR_BUS_WIDTH_EXT_LAST
+};
+
+enum mv_ddr_die_count {
+	MV_DDR_DIE_CNT_1,
+	MV_DDR_DIE_CNT_2,
+	MV_DDR_DIE_CNT_3,
+	MV_DDR_DIE_CNT_4,
+	MV_DDR_DIE_CNT_5,
+	MV_DDR_DIE_CNT_6,
+	MV_DDR_DIE_CNT_7,
+	MV_DDR_DIE_CNT_8,
+	MV_DDR_DIE_CNT_LAST
+};
+
+#define IS_ACTIVE(mask, id) \
+	((mask) & (1 << (id)))
+
+#define VALIDATE_ACTIVE(mask, id)		\
+	{					\
+	if (IS_ACTIVE(mask, id) == 0)		\
+		continue;			\
+	}
+
+#define IS_IF_ACTIVE(if_mask, if_id) \
+	((if_mask) & (1 << (if_id)))
+
+#define VALIDATE_IF_ACTIVE(mask, id)		\
+	{					\
+	if (IS_IF_ACTIVE(mask, id) == 0)	\
+		continue;			\
+	}
+
+#define IS_BUS_ACTIVE(if_mask , if_id) \
+	(((if_mask) >> (if_id)) & 1)
+
+#define VALIDATE_BUS_ACTIVE(mask, id)		\
+	{					\
+	if (IS_BUS_ACTIVE(mask, id) == 0)	\
+		continue;			\
+	}
+
+#define DDR3_IS_ECC_PUP3_MODE(if_mask)		\
+	(((if_mask) == BUS_MASK_16BIT_ECC_PUP3) ? 1 : 0)
+
+#define DDR3_IS_ECC_PUP4_MODE(if_mask)		\
+	(((if_mask) == BUS_MASK_32BIT_ECC ||	\
+	  (if_mask) == BUS_MASK_16BIT_ECC) ? 1 : 0)
+
+#define DDR3_IS_16BIT_DRAM_MODE(mask)		\
+	(((mask) == BUS_MASK_16BIT ||		\
+	  (mask) == BUS_MASK_16BIT_ECC ||	\
+	  (mask) == BUS_MASK_16BIT_ECC_PUP3) ? 1 : 0)
+
+#define DDR3_IS_ECC_PUP8_MODE(if_mask)				\
+	(((if_mask) == MV_DDR_32BIT_ECC_PUP8_BUS_MASK ||	\
+	  (if_mask) == MV_DDR_64BIT_ECC_PUP8_BUS_MASK) ? 1 : 0)
+
+#define MV_DDR_IS_64BIT_DRAM_MODE(mask)					\
+	((((mask) & MV_DDR_64BIT_BUS_MASK) == MV_DDR_64BIT_BUS_MASK) ||	\
+	 (((mask) & MV_DDR_64BIT_ECC_PUP8_BUS_MASK) == MV_DDR_64BIT_ECC_PUP8_BUS_MASK) ? 1 : 0)
+
+#define MV_DDR_IS_32BIT_IN_64BIT_DRAM_MODE(mask, sphys)		\
+	(((sphys) == 9) &&					\
+	(((mask) == BUS_MASK_32BIT) ||				\
+	 ((mask) == MV_DDR_32BIT_ECC_PUP8_BUS_MASK)) ? 1 : 0)
+
+#define MV_DDR_IS_HALF_BUS_DRAM_MODE(mask, sphys)		\
+	(MV_DDR_IS_32BIT_IN_64BIT_DRAM_MODE(mask, sphys) ||	\
+	 DDR3_IS_16BIT_DRAM_MODE(mask))
+
+struct mv_ddr_topology_map *mv_ddr_topology_map_get(void);
+unsigned int mv_ddr_cl_calc(unsigned int taa_min, unsigned int tclk);
+unsigned int mv_ddr_cwl_calc(unsigned int tclk);
+int mv_ddr_topology_map_update(void);
+unsigned short mv_ddr_bus_bit_mask_get(void);
+unsigned int mv_ddr_if_bus_width_get(void);
+unsigned int mv_ddr_cs_num_get(void);
+int mv_ddr_is_ecc_ena(void);
+unsigned long long mv_ddr_mem_sz_per_cs_get(void);
+unsigned long long mv_ddr_mem_sz_get(void);
+unsigned int mv_ddr_rtt_nom_get(void);
+unsigned int mv_ddr_rtt_park_get(void);
+unsigned int mv_ddr_rtt_wr_get(void);
+unsigned int mv_ddr_dic_get(void);
+
+#endif /* _MV_DDR_TOPOLOGY_H */
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_training_db.h b/drivers/ddr/marvell/a38x/mv_ddr_training_db.h
new file mode 100644
index 0000000..838be45
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/mv_ddr_training_db.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef _MV_DDR_TRAINING_DB_H
+#define _MV_DDR_TRAINING_DB_H
+
+#include "mv_ddr_topology.h"
+
+/* in ns */
+#define TREFI_LOW	7800
+#define TREFI_HIGH	3900
+
+enum mv_ddr_page_size {
+	MV_DDR_PAGE_SIZE_1K = 1,
+	MV_DDR_PAGE_SIZE_2K
+};
+
+struct mv_ddr_page_element {
+	/* 8-bit bus width page size */
+	enum mv_ddr_page_size page_size_8bit;
+	/* 16-bit bus width page size */
+	enum mv_ddr_page_size page_size_16bit;
+};
+
+/* cas latency value per frequency */
+struct mv_ddr_cl_val_per_freq {
+	unsigned int cl_val[MV_DDR_FREQ_LAST];
+};
+
+u32 mv_ddr_rfc_get(u32 mem);
+unsigned int *mv_ddr_freq_tbl_get(void);
+u32 mv_ddr_freq_get(enum mv_ddr_freq freq);
+u32 mv_ddr_page_size_get(enum mv_ddr_dev_width bus_width, enum mv_ddr_die_capacity mem_size);
+unsigned int mv_ddr_speed_bin_timing_get(enum mv_ddr_speed_bin index, enum mv_ddr_speed_bin_timing element);
+u32 mv_ddr_cl_val_get(u32 index, u32 freq);
+u32 mv_ddr_cwl_val_get(u32 index, u32 freq);
+
+#endif /* _MV_DDR_TRAINING_DB_H */
diff --git a/drivers/ddr/marvell/a38x/seq_exec.h b/drivers/ddr/marvell/a38x/seq_exec.h
new file mode 100644
index 0000000..fe0cb8f
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/seq_exec.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _SEQ_EXEC_H
+#define _SEQ_EXEC_H
+
+#define NA			0xff
+#define DEFAULT_PARAM		0
+#define MV_BOARD_TCLK_ERROR	0xffffffff
+
+#define NO_DATA			0xffffffff
+#define MAX_DATA_ARRAY		5
+#define FIRST_CELL		0
+
+/* Operation types */
+enum mv_op {
+	WRITE_OP,
+	DELAY_OP,
+	POLL_OP,
+};
+
+/* Operation parameters */
+struct op_params {
+	u32 unit_base_reg;
+	u32 unit_offset;
+	u32 mask;
+	u32 data[MAX_DATA_ARRAY];	/* data array */
+	u8 wait_time;			/* msec */
+	u16 num_of_loops;		/* for polling only */
+};
+
+/*
+ * Sequence parameters. Each sequence contains:
+ * 1. Sequence id.
+ * 2. Sequence size (total amount of operations during the sequence)
+ * 3. a series of operations. operations can be write, poll or delay
+ * 4. index in the data array (the entry where the relevant data sits)
+ */
+struct cfg_seq {
+	struct op_params *op_params_ptr;
+	u8 cfg_seq_size;
+	u8 data_arr_idx;
+};
+
+extern struct cfg_seq serdes_seq_db[];
+
+/*
+ * A generic function type for executing an operation (write, poll or delay)
+ */
+typedef int (*op_execute_func_ptr)(u32 serdes_num, struct op_params *params,
+				   u32 data_arr_idx);
+
+/* Specific functions for executing each operation */
+int write_op_execute(u32 serdes_num, struct op_params *params,
+		     u32 data_arr_idx);
+int delay_op_execute(u32 serdes_num, struct op_params *params,
+		     u32 data_arr_idx);
+int poll_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx);
+enum mv_op get_cfg_seq_op(struct op_params *params);
+int mv_seq_exec(u32 serdes_num, u32 seq_id);
+
+#endif /*_SEQ_EXEC_H*/
diff --git a/drivers/ddr/marvell/a38x/xor.c b/drivers/ddr/marvell/a38x/xor.c
new file mode 100644
index 0000000..5fb9e21
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/xor.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include "ddr3_init.h"
+#include "mv_ddr_common.h"
+#include "xor_regs.h"
+
+/* defines  */
+#ifdef MV_DEBUG
+#define DB(x)	x
+#else
+#define DB(x)
+#endif
+
+static u32 ui_xor_regs_ctrl_backup;
+static u32 ui_xor_regs_base_backup[MAX_CS_NUM + 1];
+static u32 ui_xor_regs_mask_backup[MAX_CS_NUM + 1];
+
+void mv_sys_xor_init(u32 num_of_cs, u32 cs_ena, uint64_t cs_size, u32 base_delta)
+{
+	u32 reg, ui, cs_count;
+	uint64_t base, size_mask;
+
+	ui_xor_regs_ctrl_backup = reg_read(XOR_WINDOW_CTRL_REG(0, 0));
+	for (ui = 0; ui < MAX_CS_NUM + 1; ui++)
+		ui_xor_regs_base_backup[ui] =
+			reg_read(XOR_BASE_ADDR_REG(0, ui));
+	for (ui = 0; ui < MAX_CS_NUM + 1; ui++)
+		ui_xor_regs_mask_backup[ui] =
+			reg_read(XOR_SIZE_MASK_REG(0, ui));
+
+	reg = 0;
+	for (ui = 0, cs_count = 0;
+	     (cs_count < num_of_cs) && (ui < 8);
+	     ui++, cs_count++) {
+		if (cs_ena & (1 << ui)) {
+			/* Enable Window x for each CS */
+			reg |= (0x1 << (ui));
+			/* Enable Window x for each CS */
+			reg |= (0x3 << ((ui * 2) + 16));
+		}
+	}
+
+	reg_write(XOR_WINDOW_CTRL_REG(0, 0), reg);
+
+	cs_count = 0;
+	for (ui = 0, cs_count = 0;
+	     (cs_count < num_of_cs) && (ui < 8);
+	     ui++, cs_count++) {
+		if (cs_ena & (1 << ui)) {
+			/*
+			 * window x - Base - 0x00000000,
+			 * Attribute 0x0e - DRAM
+			 */
+			base = cs_size * ui + base_delta;
+			/* fixed size 2GB for each CS */
+			size_mask = 0x7FFF0000;
+			switch (ui) {
+			case 0:
+				base |= 0xe00;
+				break;
+			case 1:
+				base |= 0xd00;
+				break;
+			case 2:
+				base |= 0xb00;
+				break;
+			case 3:
+				base |= 0x700;
+				break;
+			case 4: /* SRAM */
+				base = 0x40000000;
+				/* configure as shared transaction */
+				base |= 0x1F00;
+				size_mask = 0xF0000;
+				break;
+			}
+
+			reg_write(XOR_BASE_ADDR_REG(0, ui), (u32)base);
+			size_mask = (cs_size / _64K) - 1;
+			size_mask = (size_mask << XESMRX_SIZE_MASK_OFFS) & XESMRX_SIZE_MASK_MASK;
+			/* window x - Size */
+			reg_write(XOR_SIZE_MASK_REG(0, ui), (u32)size_mask);
+		}
+	}
+
+	mv_xor_hal_init(1);
+
+	return;
+}
+
+void mv_sys_xor_finish(void)
+{
+	u32 ui;
+
+	reg_write(XOR_WINDOW_CTRL_REG(0, 0), ui_xor_regs_ctrl_backup);
+	for (ui = 0; ui < MAX_CS_NUM + 1; ui++)
+		reg_write(XOR_BASE_ADDR_REG(0, ui),
+			  ui_xor_regs_base_backup[ui]);
+	for (ui = 0; ui < MAX_CS_NUM + 1; ui++)
+		reg_write(XOR_SIZE_MASK_REG(0, ui),
+			  ui_xor_regs_mask_backup[ui]);
+
+	reg_write(XOR_ADDR_OVRD_REG(0, 0), 0);
+}
+
+/*
+ * mv_xor_hal_init - Initialize XOR engine
+ *
+ * DESCRIPTION:
+ *               This function initialize XOR unit.
+ * INPUT:
+ *       None.
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise.
+ */
+void mv_xor_hal_init(u32 xor_chan_num)
+{
+	u32 i;
+
+	/* Abort any XOR activity & set default configuration */
+	for (i = 0; i < xor_chan_num; i++) {
+		mv_xor_command_set(i, MV_STOP);
+		mv_xor_ctrl_set(i, (1 << XEXCR_REG_ACC_PROTECT_OFFS) |
+				(4 << XEXCR_DST_BURST_LIMIT_OFFS) |
+				(4 << XEXCR_SRC_BURST_LIMIT_OFFS));
+	}
+}
+
+/*
+ * mv_xor_ctrl_set - Set XOR channel control registers
+ *
+ * DESCRIPTION:
+ *
+ * INPUT:
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise.
+ * NOTE:
+ *  This function does not modify the Operation_mode field of control register.
+ */
+int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl)
+{
+	u32 old_value;
+
+	/* update the XOR Engine [0..1] Configuration Registers (XEx_c_r) */
+	old_value = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan))) &
+		XEXCR_OPERATION_MODE_MASK;
+	xor_ctrl &= ~XEXCR_OPERATION_MODE_MASK;
+	xor_ctrl |= old_value;
+	reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), xor_ctrl);
+
+	return MV_OK;
+}
+
+int mv_xor_mem_init(u32 chan, u32 start_ptr, unsigned long long block_size,
+		    u32 init_val_high, u32 init_val_low)
+{
+	u32 temp;
+
+	if (block_size == _4G)
+		block_size -= 1;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN)
+		return MV_BAD_PARAM;
+
+	if (MV_ACTIVE == mv_xor_state_get(chan))
+		return MV_BUSY;
+
+	if ((block_size < XEXBSR_BLOCK_SIZE_MIN_VALUE) ||
+	    (block_size > XEXBSR_BLOCK_SIZE_MAX_VALUE))
+		return MV_BAD_PARAM;
+
+	/* set the operation mode to Memory Init */
+	temp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)));
+	temp &= ~XEXCR_OPERATION_MODE_MASK;
+	temp |= XEXCR_OPERATION_MODE_MEM_INIT;
+	reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), temp);
+
+	/*
+	 * update the start_ptr field in XOR Engine [0..1] Destination Pointer
+	 * Register
+	 */
+	reg_write(XOR_DST_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)), start_ptr);
+
+	/*
+	 * update the Block_size field in the XOR Engine[0..1] Block Size
+	 * Registers
+	 */
+	reg_write(XOR_BLOCK_SIZE_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		  block_size);
+
+	/*
+	 * update the field Init_val_l in the XOR Engine Initial Value Register
+	 * Low (XEIVRL)
+	 */
+	reg_write(XOR_INIT_VAL_LOW_REG(XOR_UNIT(chan)), init_val_low);
+
+	/*
+	 * update the field Init_val_h in the XOR Engine Initial Value Register
+	 * High (XEIVRH)
+	 */
+	reg_write(XOR_INIT_VAL_HIGH_REG(XOR_UNIT(chan)), init_val_high);
+
+	/* start transfer */
+	reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		    XEXACTR_XESTART_MASK);
+
+	return MV_OK;
+}
+
+/*
+ * mv_xor_state_get - Get XOR channel state.
+ *
+ * DESCRIPTION:
+ *       XOR channel activity state can be active, idle, paused.
+ *       This function retrunes the channel activity state.
+ *
+ * INPUT:
+ *       chan     - the channel number
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       XOR_CHANNEL_IDLE    - If the engine is idle.
+ *       XOR_CHANNEL_ACTIVE  - If the engine is busy.
+ *       XOR_CHANNEL_PAUSED  - If the engine is paused.
+ *       MV_UNDEFINED_STATE  - If the engine state is undefind or there is no
+ *                             such engine
+ */
+enum mv_state mv_xor_state_get(u32 chan)
+{
+	u32 state;
+
+	/* Parameter checking   */
+	if (chan >= MV_XOR_MAX_CHAN) {
+		DB(printf("%s: ERR. Invalid chan num %d\n", __func__, chan));
+		return MV_UNDEFINED_STATE;
+	}
+
+	/* read the current state */
+	state = reg_read(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)));
+	state &= XEXACTR_XESTATUS_MASK;
+
+	/* return the state */
+	switch (state) {
+	case XEXACTR_XESTATUS_IDLE:
+		return MV_IDLE;
+	case XEXACTR_XESTATUS_ACTIVE:
+		return MV_ACTIVE;
+	case XEXACTR_XESTATUS_PAUSED:
+		return MV_PAUSED;
+	}
+
+	return MV_UNDEFINED_STATE;
+}
+
+/*
+ * mv_xor_command_set - Set command of XOR channel
+ *
+ * DESCRIPTION:
+ *       XOR channel can be started, idle, paused and restarted.
+ *       Paused can be set only if channel is active.
+ *       Start can be set only if channel is idle or paused.
+ *       Restart can be set only if channel is paused.
+ *       Stop can be set only if channel is active.
+ *
+ * INPUT:
+ *       chan     - The channel number
+ *       command  - The command type (start, stop, restart, pause)
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       MV_OK on success , MV_BAD_PARAM on erroneous parameter, MV_ERROR on
+ *       undefind XOR engine mode
+ */
+int mv_xor_command_set(u32 chan, enum mv_command command)
+{
+	enum mv_state state;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN) {
+		DB(printf("%s: ERR. Invalid chan num %d\n", __func__, chan));
+		return MV_BAD_PARAM;
+	}
+
+	/* get the current state */
+	state = mv_xor_state_get(chan);
+
+	if ((command == MV_START) && (state == MV_IDLE)) {
+		/* command is start and current state is idle */
+		reg_bit_set(XOR_ACTIVATION_REG
+			    (XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XESTART_MASK);
+		return MV_OK;
+	} else if ((command == MV_STOP) && (state == MV_ACTIVE)) {
+		/* command is stop and current state is active */
+		reg_bit_set(XOR_ACTIVATION_REG
+			    (XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XESTOP_MASK);
+		return MV_OK;
+	} else if (((enum mv_state)command == MV_PAUSED) &&
+		   (state == MV_ACTIVE)) {
+		/* command is paused and current state is active */
+		reg_bit_set(XOR_ACTIVATION_REG
+			    (XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XEPAUSE_MASK);
+		return MV_OK;
+	} else if ((command == MV_RESTART) && (state == MV_PAUSED)) {
+		/* command is restart and current state is paused */
+		reg_bit_set(XOR_ACTIVATION_REG
+			    (XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XERESTART_MASK);
+		return MV_OK;
+	} else if ((command == MV_STOP) && (state == MV_IDLE)) {
+		/* command is stop and current state is active */
+		return MV_OK;
+	}
+
+	/* illegal command */
+	DB(printf("%s: ERR. Illegal command\n", __func__));
+
+	return MV_BAD_PARAM;
+}
+
+void ddr3_new_tip_ecc_scrub(void)
+{
+	u32 cs_c, max_cs;
+	u32 cs_ena = 0;
+	uint64_t total_mem_size, cs_mem_size = 0;
+
+	printf("DDR Training Sequence - Start scrubbing\n");
+	max_cs = mv_ddr_cs_num_get();
+	for (cs_c = 0; cs_c < max_cs; cs_c++)
+		cs_ena |= 1 << cs_c;
+
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X)
+	/* all chip-selects are of same size */
+	ddr3_calc_mem_cs_size(0, &cs_mem_size);
+#endif
+
+	mv_sys_xor_init(max_cs, cs_ena, cs_mem_size, 0);
+	total_mem_size = max_cs * cs_mem_size;
+	mv_xor_mem_init(0, 0, total_mem_size, 0xdeadbeef, 0xdeadbeef);
+	/* wait for previous transfer completion */
+	while (mv_xor_state_get(0) != MV_IDLE)
+		;
+	/* Return XOR State */
+	mv_sys_xor_finish();
+
+	printf("DDR3 Training Sequence - End scrubbing\n");
+}
+
+/*
+* mv_xor_transfer - Transfer data from source to destination in one of
+*		    three modes: XOR, CRC32 or DMA
+*
+* DESCRIPTION:
+*	This function initiates XOR channel, according to function parameters,
+*	in order to perform XOR, CRC32 or DMA transaction.
+*	To gain maximum performance the user is asked to keep the following
+*	restrictions:
+*	1) Selected engine is available (not busy).
+*	2) This module does not take into consideration CPU MMU issues.
+*	   In order for the XOR engine to access the appropriate source
+*	   and destination, address parameters must be given in system
+*	   physical mode.
+*	3) This API does not take care of cache coherency issues. The source,
+*	   destination and, in case of chain, the descriptor list are assumed
+*	   to be cache coherent.
+*	4) Parameters validity.
+*
+* INPUT:
+*	chan		- XOR channel number.
+*	type	- One of three: XOR, CRC32 and DMA operations.
+*	xor_chain_ptr	- address of chain pointer
+*
+* OUTPUT:
+*	None.
+*
+* RETURN:
+*       MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise.
+*
+*******************************************************************************/
+int mv_xor_transfer(u32 chan, enum xor_type type, u32 xor_chain_ptr)
+{
+	u32 temp;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN) {
+		DB(printf("%s: ERR. Invalid chan num %d\n", __func__, chan));
+		return MV_BAD_PARAM;
+	}
+	if (mv_xor_state_get(chan) == MV_ACTIVE) {
+		DB(printf("%s: ERR. Channel is already active\n", __func__));
+		return MV_BUSY;
+	}
+	if (xor_chain_ptr == 0x0) {
+		DB(printf("%s: ERR. xor_chain_ptr is NULL pointer\n", __func__));
+		return MV_BAD_PARAM;
+	}
+
+	/* read configuration register and mask the operation mode field */
+	temp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)));
+	temp &= ~XEXCR_OPERATION_MODE_MASK;
+
+	switch (type) {
+	case MV_XOR:
+		if ((xor_chain_ptr & XEXDPR_DST_PTR_XOR_MASK) != 0) {
+			DB(printf("%s: ERR. Invalid chain pointer (bits [5:0] must be cleared)\n",
+				  __func__));
+			return MV_BAD_PARAM;
+		}
+		/* set the operation mode to XOR */
+		temp |= XEXCR_OPERATION_MODE_XOR;
+		break;
+	case MV_DMA:
+		if ((xor_chain_ptr & XEXDPR_DST_PTR_DMA_MASK) != 0) {
+			DB(printf("%s: ERR. Invalid chain pointer (bits [4:0] must be cleared)\n",
+				  __func__));
+			return MV_BAD_PARAM;
+		}
+		/* set the operation mode to DMA */
+		temp |= XEXCR_OPERATION_MODE_DMA;
+		break;
+	case MV_CRC32:
+		if ((xor_chain_ptr & XEXDPR_DST_PTR_CRC_MASK) != 0) {
+			DB(printf("%s: ERR. Invalid chain pointer (bits [4:0] must be cleared)\n",
+				  __func__));
+			return MV_BAD_PARAM;
+		}
+		/* set the operation mode to CRC32 */
+		temp |= XEXCR_OPERATION_MODE_CRC;
+		break;
+	default:
+		return MV_BAD_PARAM;
+	}
+
+	/* write the operation mode to the register */
+	reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), temp);
+	/*
+	 * update the NextDescPtr field in the XOR Engine [0..1] Next Descriptor
+	 * Pointer Register (XExNDPR)
+	 */
+	reg_write(XOR_NEXT_DESC_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		  xor_chain_ptr);
+
+	/* start transfer */
+	reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		    XEXACTR_XESTART_MASK);
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/a38x/xor.h b/drivers/ddr/marvell/a38x/xor.h
new file mode 100644
index 0000000..1e02650
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/xor.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _XOR_H
+#define _XOR_H
+
+#define SRAM_BASE		0x40000000
+
+#define MV_XOR_MAX_UNIT		2	/* XOR unit == XOR engine */
+#define MV_XOR_MAX_CHAN		4	/* total channels for all units */
+#define MV_XOR_MAX_CHAN_PER_UNIT 2	/* channels for units */
+
+#define MV_IS_POWER_OF_2(num)	(((num) != 0) && (((num) & ((num) - 1)) == 0))
+
+/*
+ * This structure describes address space window. Window base can be
+ * 64 bit, window size up to 4GB
+ */
+struct addr_win {
+	u32 base_low;		/* 32bit base low       */
+	u32 base_high;		/* 32bit base high      */
+	u32 size;		/* 32bit size           */
+};
+
+/* This structure describes SoC units address decode window	*/
+struct unit_win_info {
+	struct addr_win addr_win;	/* An address window */
+	int enable;		/* Address decode window is enabled/disabled  */
+	u8 attrib;		/* chip select attributes */
+	u8 target_id;		/* Target Id of this MV_TARGET */
+};
+
+/*
+ * This enumerator describes the type of functionality the XOR channel
+ * can have while using the same data structures.
+ */
+enum xor_type {
+	MV_XOR,			/* XOR channel functions as XOR accelerator   */
+	MV_DMA,			/* XOR channel functions as IDMA channel      */
+	MV_CRC32		/* XOR channel functions as CRC 32 calculator */
+};
+
+enum mv_state {
+	MV_IDLE,
+	MV_ACTIVE,
+	MV_PAUSED,
+	MV_UNDEFINED_STATE
+};
+
+/*
+ * This enumerator describes the set of commands that can be applied on
+ * an engine (e.g. IDMA, XOR). Appling a comman depends on the current
+ * status (see MV_STATE enumerator)
+ *
+ * Start can be applied only when status is IDLE
+ * Stop can be applied only when status is IDLE, ACTIVE or PAUSED
+ * Pause can be applied only when status is ACTIVE
+ * Restart can be applied only when status is PAUSED
+ */
+enum mv_command {
+	MV_START,		/* Start     */
+	MV_STOP,		/* Stop     */
+	MV_PAUSE,		/* Pause    */
+	MV_RESTART		/* Restart  */
+};
+
+enum xor_override_target {
+	SRC_ADDR0,		/* Source Address #0 Control */
+	SRC_ADDR1,		/* Source Address #1 Control */
+	SRC_ADDR2,		/* Source Address #2 Control */
+	SRC_ADDR3,		/* Source Address #3 Control */
+	SRC_ADDR4,		/* Source Address #4 Control */
+	SRC_ADDR5,		/* Source Address #5 Control */
+	SRC_ADDR6,		/* Source Address #6 Control */
+	SRC_ADDR7,		/* Source Address #7 Control */
+	XOR_DST_ADDR,		/* Destination Address Control */
+	XOR_NEXT_DESC		/* Next Descriptor Address Control */
+};
+
+enum mv_state mv_xor_state_get(u32 chan);
+void mv_xor_hal_init(u32 xor_chan_num);
+int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl);
+int mv_xor_command_set(u32 chan, enum mv_command command);
+int mv_xor_override_set(u32 chan, enum xor_override_target target, u32 win_num,
+			int enable);
+int mv_xor_transfer(u32 chan, enum xor_type type, u32 xor_chain_ptr);
+
+#endif
diff --git a/drivers/ddr/marvell/a38x/xor_regs.h b/drivers/ddr/marvell/a38x/xor_regs.h
new file mode 100644
index 0000000..fdfc0f2
--- /dev/null
+++ b/drivers/ddr/marvell/a38x/xor_regs.h
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef _XOR_REGS_h
+#define _XOR_REGS_h
+
+/*
+ * For controllers that have two XOR units, then chans 2 & 3 will be
+ * mapped to channels 0 & 1 of unit 1
+ */
+#define XOR_UNIT(chan)	((chan) >> 1)
+#define XOR_CHAN(chan)  ((chan) & 1)
+
+#define MV_XOR_REGS_OFFSET(unit)	(0x60900)
+#define MV_XOR_REGS_BASE(unit)		(MV_XOR_REGS_OFFSET(unit))
+
+/* XOR Engine Control Register Map */
+#define XOR_CHANNEL_ARBITER_REG(unit)	(MV_XOR_REGS_BASE(unit))
+#define XOR_CONFIG_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + \
+					 (0x10 + ((chan) * 4)))
+#define XOR_ACTIVATION_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + \
+					 (0x20 + ((chan) * 4)))
+
+/* XOR Engine Interrupt Register Map */
+#define XOR_CAUSE_REG(unit)		(MV_XOR_REGS_BASE(unit)+(0x30))
+#define XOR_MASK_REG(unit)		(MV_XOR_REGS_BASE(unit)+(0x40))
+#define XOR_ERROR_CAUSE_REG(unit)	(MV_XOR_REGS_BASE(unit)+(0x50))
+#define XOR_ERROR_ADDR_REG(unit)	(MV_XOR_REGS_BASE(unit)+(0x60))
+
+/* XOR Engine Descriptor Register Map */
+#define XOR_NEXT_DESC_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \
+					   (0x200 + ((chan) * 4)))
+#define XOR_CURR_DESC_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \
+					   (0x210 + ((chan) * 4)))
+#define XOR_BYTE_COUNT_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + \
+					 (0x220 + ((chan) * 4)))
+
+/* XOR Engine ECC/Mem_init Register Map */
+#define XOR_DST_PTR_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + \
+					 (0x2b0 + ((chan) * 4)))
+#define XOR_BLOCK_SIZE_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + \
+					 (0x2c0 + ((chan) * 4)))
+#define XOR_TIMER_MODE_CTRL_REG(unit)	(MV_XOR_REGS_BASE(unit) + (0x2d0))
+#define XOR_TIMER_MODE_INIT_VAL_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2d4))
+#define XOR_TIMER_MODE_CURR_VAL_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2d8))
+#define XOR_INIT_VAL_LOW_REG(unit)	(MV_XOR_REGS_BASE(unit) + (0x2e0))
+#define XOR_INIT_VAL_HIGH_REG(unit)	(MV_XOR_REGS_BASE(unit) + (0x2e4))
+
+/* XOR Engine Debug Register Map */
+#define XOR_DEBUG_REG(unit)		(MV_XOR_REGS_BASE(unit) + (0x70))
+
+/* XOR register fileds */
+
+/* XOR Engine Channel Arbiter Register */
+#define XECAR_SLICE_OFFS(slice_num)	(slice_num)
+#define XECAR_SLICE_MASK(slice_num)	(1 << (XECAR_SLICE_OFFS(slice_num)))
+
+/* XOR Engine [0..1] Configuration Registers */
+#define XEXCR_OPERATION_MODE_OFFS	(0)
+#define XEXCR_OPERATION_MODE_MASK	(7 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_XOR	(0 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_CRC	(1 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_DMA	(2 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_ECC	(3 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_MEM_INIT	(4 << XEXCR_OPERATION_MODE_OFFS)
+
+#define XEXCR_SRC_BURST_LIMIT_OFFS	(4)
+#define XEXCR_SRC_BURST_LIMIT_MASK	(7 << XEXCR_SRC_BURST_LIMIT_OFFS)
+#define XEXCR_DST_BURST_LIMIT_OFFS	(8)
+#define XEXCR_DST_BURST_LIMIT_MASK	(7 << XEXCR_DST_BURST_LIMIT_OFFS)
+#define XEXCR_DRD_RES_SWP_OFFS		(12)
+#define XEXCR_DRD_RES_SWP_MASK		(1 << XEXCR_DRD_RES_SWP_OFFS)
+#define XEXCR_DWR_REQ_SWP_OFFS		(13)
+#define XEXCR_DWR_REQ_SWP_MASK		(1 << XEXCR_DWR_REQ_SWP_OFFS)
+#define XEXCR_DES_SWP_OFFS		(14)
+#define XEXCR_DES_SWP_MASK		(1 << XEXCR_DES_SWP_OFFS)
+#define XEXCR_REG_ACC_PROTECT_OFFS	(15)
+#define XEXCR_REG_ACC_PROTECT_MASK	(1 << XEXCR_REG_ACC_PROTECT_OFFS)
+
+/* XOR Engine [0..1] Activation Registers */
+#define XEXACTR_XESTART_OFFS		(0)
+#define XEXACTR_XESTART_MASK		(1 << XEXACTR_XESTART_OFFS)
+#define XEXACTR_XESTOP_OFFS		(1)
+#define XEXACTR_XESTOP_MASK		(1 << XEXACTR_XESTOP_OFFS)
+#define XEXACTR_XEPAUSE_OFFS		(2)
+#define XEXACTR_XEPAUSE_MASK		(1 << XEXACTR_XEPAUSE_OFFS)
+#define XEXACTR_XERESTART_OFFS		(3)
+#define XEXACTR_XERESTART_MASK		(1 << XEXACTR_XERESTART_OFFS)
+#define XEXACTR_XESTATUS_OFFS		(4)
+#define XEXACTR_XESTATUS_MASK		(3 << XEXACTR_XESTATUS_OFFS)
+#define XEXACTR_XESTATUS_IDLE		(0 << XEXACTR_XESTATUS_OFFS)
+#define XEXACTR_XESTATUS_ACTIVE		(1 << XEXACTR_XESTATUS_OFFS)
+#define XEXACTR_XESTATUS_PAUSED		(2 << XEXACTR_XESTATUS_OFFS)
+
+/* XOR Engine Interrupt Cause Register (XEICR) */
+#define XEICR_CHAN_OFFS			16
+#define XEICR_CAUSE_OFFS(chan)		(chan * XEICR_CHAN_OFFS)
+#define XEICR_CAUSE_MASK(chan, cause)	(1 << (cause + XEICR_CAUSE_OFFS(chan)))
+#define XEICR_COMP_MASK_ALL		0x000f000f
+#define XEICR_COMP_MASK(chan)		(0x000f << XEICR_CAUSE_OFFS(chan))
+#define XEICR_ERR_MASK			0x03800380
+
+/* XOR Engine Error Cause Register (XEECR) */
+#define XEECR_ERR_TYPE_OFFS		0
+#define XEECR_ERR_TYPE_MASK		(0x1f << XEECR_ERR_TYPE_OFFS)
+
+/* XOR Engine Error Address Register (XEEAR) */
+#define XEEAR_ERR_ADDR_OFFS		(0)
+#define XEEAR_ERR_ADDR_MASK		(0xffffffff << XEEAR_ERR_ADDR_OFFS)
+
+/* XOR Engine [0..1] Next Descriptor Pointer Register */
+#define XEXNDPR_NEXT_DESC_PTR_OFFS	(0)
+#define XEXNDPR_NEXT_DESC_PTR_MASK	(0xffffffff << \
+					 XEXNDPR_NEXT_DESC_PTR_OFFS)
+
+/* XOR Engine [0..1] Current Descriptor Pointer Register */
+#define XEXCDPR_CURRENT_DESC_PTR_OFFS	(0)
+#define XEXCDPR_CURRENT_DESC_PTR_MASK	(0xffffffff << \
+					 XEXCDPR_CURRENT_DESC_PTR_OFFS)
+
+/* XOR Engine [0..1] Byte Count Register */
+#define XEXBCR_BYTE_CNT_OFFS		(0)
+#define XEXBCR_BYTE_CNT_MASK		(0xffffffff << XEXBCR_BYTE_CNT_OFFS)
+
+/* XOR Engine [0..1] Destination Pointer Register */
+#define XEXDPR_DST_PTR_OFFS		(0)
+#define XEXDPR_DST_PTR_MASK		(0xffffffff << XEXDPR_DST_PTR_OFFS)
+#define XEXDPR_DST_PTR_XOR_MASK		(0x3f)
+#define XEXDPR_DST_PTR_DMA_MASK		(0x1f)
+#define XEXDPR_DST_PTR_CRC_MASK		(0x1f)
+
+/* XOR Engine[0..1] Block Size Registers */
+#define XEXBSR_BLOCK_SIZE_OFFS		(0)
+#define XEXBSR_BLOCK_SIZE_MASK		(0xffffffff << XEXBSR_BLOCK_SIZE_OFFS)
+#define XEXBSR_BLOCK_SIZE_MIN_VALUE	(128)
+#define XEXBSR_BLOCK_SIZE_MAX_VALUE	(0xffffffff)
+
+/* XOR Engine Timer Mode Control Register (XETMCR) */
+#define XETMCR_TIMER_EN_OFFS		(0)
+#define XETMCR_TIMER_EN_MASK		(1 << XETMCR_TIMER_EN_OFFS)
+#define XETMCR_TIMER_EN_ENABLE		(1 << XETMCR_TIMER_EN_OFFS)
+#define XETMCR_TIMER_EN_DISABLE		(0 << XETMCR_TIMER_EN_OFFS)
+#define XETMCR_SECTION_SIZE_CTRL_OFFS	(8)
+#define XETMCR_SECTION_SIZE_CTRL_MASK	(0x1f << XETMCR_SECTION_SIZE_CTRL_OFFS)
+#define XETMCR_SECTION_SIZE_MIN_VALUE	(7)
+#define XETMCR_SECTION_SIZE_MAX_VALUE	(31)
+
+/* XOR Engine Timer Mode Initial Value Register (XETMIVR) */
+#define XETMIVR_TIMER_INIT_VAL_OFFS	(0)
+#define XETMIVR_TIMER_INIT_VAL_MASK	(0xffffffff << \
+					 XETMIVR_TIMER_INIT_VAL_OFFS)
+
+/* XOR Engine Timer Mode Current Value Register (XETMCVR) */
+#define XETMCVR_TIMER_CRNT_VAL_OFFS	(0)
+#define XETMCVR_TIMER_CRNT_VAL_MASK	(0xffffffff << \
+					 XETMCVR_TIMER_CRNT_VAL_OFFS)
+
+/* XOR Engine Initial Value Register Low (XEIVRL) */
+#define XEIVRL_INIT_VAL_L_OFFS		(0)
+#define XEIVRL_INIT_VAL_L_MASK		(0xffffffff << XEIVRL_INIT_VAL_L_OFFS)
+
+/* XOR Engine Initial Value Register High (XEIVRH) */
+#define XEIVRH_INIT_VAL_H_OFFS		(0)
+#define XEIVRH_INIT_VAL_H_MASK		(0xffffffff << XEIVRH_INIT_VAL_H_OFFS)
+
+/* XOR Engine Debug Register (XEDBR) */
+#define XEDBR_PARITY_ERR_INSR_OFFS	(0)
+#define XEDBR_PARITY_ERR_INSR_MASK	(1 << XEDBR_PARITY_ERR_INSR_OFFS)
+#define XEDBR_XBAR_ERR_INSR_OFFS	(1)
+#define XEDBR_XBAR_ERR_INSR_MASK	(1 << XEDBR_XBAR_ERR_INSR_OFFS)
+
+/* XOR Engine address decode registers.	*/
+/* Maximum address decode windows */
+#define XOR_MAX_ADDR_DEC_WIN		8
+/* Maximum address arbiter windows */
+#define XOR_MAX_REMAP_WIN		4
+
+/* XOR Engine Address Decoding Register Map */
+#define XOR_WINDOW_CTRL_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + \
+					 (0x240 + ((chan) * 4)))
+#define XOR_BASE_ADDR_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \
+					  (0x250 + ((win_num) * 4)))
+#define XOR_SIZE_MASK_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \
+					  (0x270 + ((win_num) * 4)))
+#define XOR_HIGH_ADDR_REMAP_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \
+						(0x290 + ((win_num) * 4)))
+#define XOR_ADDR_OVRD_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \
+					  (0x2a0 + ((win_num) * 4)))
+
+/* XOR Engine [0..1] Window Control Registers */
+#define XEXWCR_WIN_EN_OFFS(win_num)	(win_num)
+#define XEXWCR_WIN_EN_MASK(win_num)	(1 << (XEXWCR_WIN_EN_OFFS(win_num)))
+#define XEXWCR_WIN_EN_ENABLE(win_num)	(1 << (XEXWCR_WIN_EN_OFFS(win_num)))
+#define XEXWCR_WIN_EN_DISABLE(win_num)	(0 << (XEXWCR_WIN_EN_OFFS(win_num)))
+
+#define XEXWCR_WIN_ACC_OFFS(win_num)	((2 * win_num) + 16)
+#define XEXWCR_WIN_ACC_MASK(win_num)	(3 << (XEXWCR_WIN_ACC_OFFS(win_num)))
+#define XEXWCR_WIN_ACC_NO_ACC(win_num)	(0 << (XEXWCR_WIN_ACC_OFFS(win_num)))
+#define XEXWCR_WIN_ACC_RO(win_num)	(1 << (XEXWCR_WIN_ACC_OFFS(win_num)))
+#define XEXWCR_WIN_ACC_RW(win_num)	(3 << (XEXWCR_WIN_ACC_OFFS(win_num)))
+
+/* XOR Engine Base Address Registers (XEBARx) */
+#define XEBARX_TARGET_OFFS		(0)
+#define XEBARX_TARGET_MASK		(0xf << XEBARX_TARGET_OFFS)
+#define XEBARX_ATTR_OFFS		(8)
+#define XEBARX_ATTR_MASK		(0xff << XEBARX_ATTR_OFFS)
+#define XEBARX_BASE_OFFS		(16)
+#define XEBARX_BASE_MASK		(0xffff << XEBARX_BASE_OFFS)
+
+/* XOR Engine Size Mask Registers (XESMRx) */
+#define XESMRX_SIZE_MASK_OFFS		(16)
+#define XESMRX_SIZE_MASK_MASK		(0xffff << XESMRX_SIZE_MASK_OFFS)
+#define XOR_WIN_SIZE_ALIGN		_64K
+
+/* XOR Engine High Address Remap Register (XEHARRx1) */
+#define XEHARRX_REMAP_OFFS		(0)
+#define XEHARRX_REMAP_MASK		(0xffffffff << XEHARRX_REMAP_OFFS)
+
+#define XOR_OVERRIDE_CTRL_REG(chan)	(MV_XOR_REGS_BASE(XOR_UNIT(chan)) + \
+					 (0x2a0 + ((XOR_CHAN(chan)) * 4)))
+
+/* XOR Engine [0..1] Address Override Control Register */
+#define XEXAOCR_OVR_EN_OFFS(target)	(3 * target)
+#define XEXAOCR_OVR_EN_MASK(target)	(1 << (XEXAOCR_OVR_EN_OFFS(target)))
+#define XEXAOCR_OVR_PTR_OFFS(target)	((3 * target) + 1)
+#define XEXAOCR_OVR_PTR_MASK(target)	(3 << (XEXAOCR_OVR_PTR_OFFS(target)))
+#define XEXAOCR_OVR_BAR(win_num, target) (win_num << \
+					  (XEXAOCR_OVR_PTR_OFFS(target)))
+
+/* Maximum address override windows */
+#define XOR_MAX_OVERRIDE_WIN		4
+
+#endif /* _XOR_REGS_h */
diff --git a/drivers/ddr/marvell/axp/Makefile b/drivers/ddr/marvell/axp/Makefile
new file mode 100644
index 0000000..d04d9a2
--- /dev/null
+++ b/drivers/ddr/marvell/axp/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_SPL_BUILD) += ddr3_dfs.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_dqs.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_hw_training.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_init.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_pbs.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_read_leveling.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_sdram.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_spd.o
+obj-$(CONFIG_SPL_BUILD) += ddr3_write_leveling.o
+obj-$(CONFIG_SPL_BUILD) += xor.o
diff --git a/drivers/ddr/marvell/axp/ddr3_axp.h b/drivers/ddr/marvell/axp/ddr3_axp.h
new file mode 100644
index 0000000..270691e
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_axp.h
@@ -0,0 +1,512 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __DDR3_AXP_H
+#define __DDR3_AXP_H
+
+#define MV_78XX0_Z1_REV			0x0
+#define MV_78XX0_A0_REV			0x1
+#define MV_78XX0_B0_REV			0x2
+
+#define SAR_DDR3_FREQ_MASK		0xFE00000
+#define SAR_CPU_FAB_GET(cpu, fab)	(((cpu & 0x7) << 21) | ((fab & 0xF) << 24))
+
+#define MAX_CS				4
+
+#define MIN_DIMM_ADDR			0x50
+#define FAR_END_DIMM_ADDR		0x50
+#define MAX_DIMM_ADDR			0x60
+
+#ifndef CONFIG_DDR_FIXED_SIZE
+#define SDRAM_CS_SIZE			0xFFFFFFF
+#else
+#define SDRAM_CS_SIZE			(CONFIG_DDR_FIXED_SIZE - 1)
+#endif
+#define SDRAM_CS_BASE			0x0
+#define SDRAM_DIMM_SIZE			0x80000000
+
+#define CPU_CONFIGURATION_REG(id)	(0x21800 + (id * 0x100))
+#define CPU_MRVL_ID_OFFSET		0x10
+#define SAR1_CPU_CORE_MASK		0x00000018
+#define SAR1_CPU_CORE_OFFSET		3
+
+/* Only enable ECC if the board selects it */
+#ifdef CONFIG_BOARD_ECC_SUPPORT
+#define ECC_SUPPORT
+#endif
+#define NEW_FABRIC_TWSI_ADDR		0x4E
+#ifdef CONFIG_DB_784MP_GP
+#define BUS_WIDTH_ECC_TWSI_ADDR		0x4E
+#else
+#define BUS_WIDTH_ECC_TWSI_ADDR		0x4F
+#endif
+#define MV_MAX_DDR3_STATIC_SIZE		50
+#define MV_DDR3_MODES_NUMBER		30
+
+#define RESUME_RL_PATTERNS_ADDR		(0xFE0000)
+#define RESUME_RL_PATTERNS_SIZE		(0x100)
+#define RESUME_TRAINING_VALUES_ADDR	(RESUME_RL_PATTERNS_ADDR + RESUME_RL_PATTERNS_SIZE)
+#define RESUME_TRAINING_VALUES_MAX	(0xCD0)
+#define BOOT_INFO_ADDR			(RESUME_RL_PATTERNS_ADDR + 0x1000)
+#define CHECKSUM_RESULT_ADDR		(BOOT_INFO_ADDR + 0x1000)
+#define NUM_OF_REGISTER_ADDR		(CHECKSUM_RESULT_ADDR + 4)
+#define SUSPEND_MAGIC_WORD		(0xDEADB002)
+#define REGISTER_LIST_END		(0xFFFFFFFF)
+
+/*
+ * Registers offset
+ */
+
+#define REG_SAMPLE_RESET_LOW_ADDR		0x18230
+#define REG_SAMPLE_RESET_HIGH_ADDR		0x18234
+#define	REG_SAMPLE_RESET_CPU_FREQ_OFFS		21
+#define	REG_SAMPLE_RESET_CPU_FREQ_MASK		0x00E00000
+#define	REG_SAMPLE_RESET_FAB_OFFS		24
+#define	REG_SAMPLE_RESET_FAB_MASK		0xF000000
+#define	REG_SAMPLE_RESET_TCLK_OFFS		28
+#define	REG_SAMPLE_RESET_CPU_ARCH_OFFS		31
+#define	REG_SAMPLE_RESET_HIGH_CPU_FREQ_OFFS	20
+
+/* MISC */
+/*
+ * In mainline U-Boot we're re-configuring the mvebu base address
+ * register to 0xf1000000. So need to use this value for the DDR
+ * training code as well.
+ */
+#define INTER_REGS_BASE				SOC_REGS_PHY_BASE
+
+/* DDR */
+#define REG_SDRAM_CONFIG_ADDR			0x1400
+#define REG_SDRAM_CONFIG_MASK			0x9FFFFFFF
+#define REG_SDRAM_CONFIG_RFRS_MASK		0x3FFF
+#define REG_SDRAM_CONFIG_WIDTH_OFFS		15
+#define REG_SDRAM_CONFIG_REGDIMM_OFFS		17
+#define REG_SDRAM_CONFIG_ECC_OFFS		18
+#define REG_SDRAM_CONFIG_IERR_OFFS		19
+#define REG_SDRAM_CONFIG_PUPRSTDIV_OFFS		28
+#define REG_SDRAM_CONFIG_RSTRD_OFFS		30
+
+#define REG_DUNIT_CTRL_LOW_ADDR			0x1404
+#define REG_DUNIT_CTRL_LOW_2T_OFFS		3
+#define REG_DUNIT_CTRL_LOW_2T_MASK		0x3
+#define REG_DUNIT_CTRL_LOW_DPDE_OFFS		14
+
+#define REG_SDRAM_TIMING_LOW_ADDR		0x1408
+
+#define REG_SDRAM_TIMING_HIGH_ADDR		0x140C
+#define REG_SDRAM_TIMING_H_R2R_OFFS		7
+#define REG_SDRAM_TIMING_H_R2R_MASK		0x3
+#define REG_SDRAM_TIMING_H_R2W_W2R_OFFS		9
+#define REG_SDRAM_TIMING_H_R2W_W2R_MASK		0x3
+#define REG_SDRAM_TIMING_H_W2W_OFFS		11
+#define REG_SDRAM_TIMING_H_W2W_MASK		0x1F
+#define REG_SDRAM_TIMING_H_R2R_H_OFFS		19
+#define REG_SDRAM_TIMING_H_R2R_H_MASK		0x7
+#define REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS	22
+#define REG_SDRAM_TIMING_H_R2W_W2R_H_MASK	0x7
+
+#define REG_SDRAM_ADDRESS_CTRL_ADDR		0x1410
+#define REG_SDRAM_ADDRESS_SIZE_OFFS		2
+#define REG_SDRAM_ADDRESS_SIZE_HIGH_OFFS	18
+#define REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS	4
+
+#define REG_SDRAM_OPEN_PAGES_ADDR		0x1414
+#define REG_SDRAM_OPERATION_CS_OFFS		8
+
+#define REG_SDRAM_OPERATION_ADDR		0x1418
+#define REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS	24
+#define REG_SDRAM_OPERATION_CWA_DATA_OFFS	20
+#define REG_SDRAM_OPERATION_CWA_DATA_MASK	0xF
+#define REG_SDRAM_OPERATION_CWA_RC_OFFS		16
+#define REG_SDRAM_OPERATION_CWA_RC_MASK		0xF
+#define REG_SDRAM_OPERATION_CMD_MR0		0xF03
+#define REG_SDRAM_OPERATION_CMD_MR1		0xF04
+#define REG_SDRAM_OPERATION_CMD_MR2		0xF08
+#define REG_SDRAM_OPERATION_CMD_MR3		0xF09
+#define REG_SDRAM_OPERATION_CMD_RFRS		0xF02
+#define REG_SDRAM_OPERATION_CMD_CWA		0xF0E
+#define REG_SDRAM_OPERATION_CMD_RFRS_DONE	0xF
+#define REG_SDRAM_OPERATION_CMD_MASK		0xF
+#define REG_SDRAM_OPERATION_CS_OFFS		8
+
+#define REG_OUDDR3_TIMING_ADDR			0x142C
+
+#define REG_SDRAM_MODE_ADDR			0x141C
+
+#define REG_SDRAM_EXT_MODE_ADDR			0x1420
+
+#define REG_DDR_CONT_HIGH_ADDR			0x1424
+
+#define REG_ODT_TIME_LOW_ADDR			0x1428
+#define REG_ODT_ON_CTL_RD_OFFS                  12
+#define REG_ODT_OFF_CTL_RD_OFFS                 16
+#define REG_SDRAM_ERROR_ADDR			0x1454
+#define REG_SDRAM_AUTO_PWR_SAVE_ADDR		0x1474
+#define REG_ODT_TIME_HIGH_ADDR			0x147C
+
+#define REG_SDRAM_INIT_CTRL_ADDR		0x1480
+#define REG_SDRAM_INIT_CTRL_OFFS		0
+#define REG_SDRAM_INIT_CKE_ASSERT_OFFS		2
+#define REG_SDRAM_INIT_RESET_DEASSERT_OFFS	3
+
+#define REG_SDRAM_ODT_CTRL_LOW_ADDR		0x1494
+
+#define REG_SDRAM_ODT_CTRL_HIGH_ADDR		0x1498
+/*#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK	0xFFFFFF55 */
+#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK	0x0
+#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_ENA	0x3
+
+#define REG_DUNIT_ODT_CTRL_ADDR			0x149C
+#define REG_DUNIT_ODT_CTRL_OVRD_OFFS            8
+#define REG_DUNIT_ODT_CTRL_OVRD_VAL_OFFS        9
+
+#define REG_DRAM_FIFO_CTRL_ADDR			0x14A0
+
+#define REG_DRAM_AXI_CTRL_ADDR			0x14A8
+#define REG_DRAM_AXI_CTRL_AXIDATABUSWIDTH_OFFS	0
+
+#define REG_METAL_MASK_ADDR			0x14B0
+#define REG_METAL_MASK_MASK			0xDFFFFFFF
+#define REG_METAL_MASK_RETRY_OFFS		0
+
+#define REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR	0x14C0
+
+#define REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR	0x14C4
+#define REG_DRAM_VER_CAL_MACHINE_CTRL_ADDR	0x14c8
+#define REG_DRAM_MAIN_PADS_CAL_ADDR		0x14CC
+
+#define REG_DRAM_HOR_CAL_MACHINE_CTRL_ADDR	0x17c8
+
+#define REG_CS_SIZE_SCRATCH_ADDR		0x1504
+#define REG_DYNAMIC_POWER_SAVE_ADDR		0x1520
+#define REG_DDR_IO_ADDR				0x1524
+#define REG_DDR_IO_CLK_RATIO_OFFS		15
+
+#define REG_DFS_ADDR				0x1528
+#define REG_DFS_DLLNEXTSTATE_OFFS		0
+#define REG_DFS_BLOCK_OFFS			1
+#define REG_DFS_SR_OFFS				2
+#define REG_DFS_ATSR_OFFS			3
+#define REG_DFS_RECONF_OFFS			4
+#define REG_DFS_CL_NEXT_STATE_OFFS		8
+#define REG_DFS_CL_NEXT_STATE_MASK		0xF
+#define REG_DFS_CWL_NEXT_STATE_OFFS		12
+#define REG_DFS_CWL_NEXT_STATE_MASK		0x7
+
+#define REG_READ_DATA_SAMPLE_DELAYS_ADDR	0x1538
+#define REG_READ_DATA_SAMPLE_DELAYS_MASK	0x1F
+#define REG_READ_DATA_SAMPLE_DELAYS_OFFS	8
+
+#define REG_READ_DATA_READY_DELAYS_ADDR		0x153C
+#define REG_READ_DATA_READY_DELAYS_MASK		0x1F
+#define REG_READ_DATA_READY_DELAYS_OFFS		8
+
+#define START_BURST_IN_ADDR			1
+
+#define REG_DRAM_TRAINING_SHADOW_ADDR		0x18488
+#define REG_DRAM_TRAINING_ADDR			0x15B0
+#define REG_DRAM_TRAINING_LOW_FREQ_OFFS		0
+#define REG_DRAM_TRAINING_PATTERNS_OFFS		4
+#define REG_DRAM_TRAINING_MED_FREQ_OFFS		2
+#define REG_DRAM_TRAINING_WL_OFFS		3
+#define REG_DRAM_TRAINING_RL_OFFS		6
+#define REG_DRAM_TRAINING_DQS_RX_OFFS		15
+#define REG_DRAM_TRAINING_DQS_TX_OFFS		16
+#define REG_DRAM_TRAINING_CS_OFFS		20
+#define REG_DRAM_TRAINING_RETEST_OFFS		24
+#define REG_DRAM_TRAINING_DFS_FREQ_OFFS		27
+#define REG_DRAM_TRAINING_DFS_REQ_OFFS		29
+#define REG_DRAM_TRAINING_ERROR_OFFS		30
+#define REG_DRAM_TRAINING_AUTO_OFFS		31
+#define REG_DRAM_TRAINING_RETEST_PAR		0x3
+#define REG_DRAM_TRAINING_RETEST_MASK		0xF8FFFFFF
+#define REG_DRAM_TRAINING_CS_MASK		0xFF0FFFFF
+#define REG_DRAM_TRAINING_PATTERNS_MASK		0xFF0F0000
+
+#define REG_DRAM_TRAINING_1_ADDR		0x15B4
+#define REG_DRAM_TRAINING_1_TRNBPOINT_OFFS	16
+
+#define REG_DRAM_TRAINING_2_ADDR		0x15B8
+#define REG_DRAM_TRAINING_2_OVERRUN_OFFS	17
+#define REG_DRAM_TRAINING_2_FIFO_RST_OFFS	4
+#define REG_DRAM_TRAINING_2_RL_MODE_OFFS	3
+#define REG_DRAM_TRAINING_2_WL_MODE_OFFS	2
+#define REG_DRAM_TRAINING_2_ECC_MUX_OFFS	1
+#define REG_DRAM_TRAINING_2_SW_OVRD_OFFS	0
+
+#define REG_DRAM_TRAINING_PATTERN_BASE_ADDR	0x15BC
+#define REG_DRAM_TRAINING_PATTERN_BASE_OFFS	3
+
+#define REG_TRAINING_DEBUG_2_ADDR		0x15C4
+#define REG_TRAINING_DEBUG_2_OFFS		16
+#define REG_TRAINING_DEBUG_2_MASK		0x3
+
+#define REG_TRAINING_DEBUG_3_ADDR		0x15C8
+#define REG_TRAINING_DEBUG_3_OFFS		3
+#define REG_TRAINING_DEBUG_3_MASK		0x7
+
+#define	MR_CS_ADDR_OFFS				4
+
+#define	REG_DDR3_MR0_ADDR			0x15D0
+#define	REG_DDR3_MR0_CS_ADDR			0x1870
+#define REG_DDR3_MR0_CL_MASK			0x74
+#define	REG_DDR3_MR0_CL_OFFS			2
+#define	REG_DDR3_MR0_CL_HIGH_OFFS		3
+#define	CL_MASK					0xF
+
+#define	REG_DDR3_MR1_ADDR			0x15D4
+#define	REG_DDR3_MR1_CS_ADDR			0x1874
+#define REG_DDR3_MR1_RTT_MASK			0xFFFFFDBB
+#define REG_DDR3_MR1_DLL_ENA_OFFS		0
+#define REG_DDR3_MR1_RTT_DISABLED		0x0
+#define REG_DDR3_MR1_RTT_RZQ2			0x40
+#define REG_DDR3_MR1_RTT_RZQ4			0x2
+#define REG_DDR3_MR1_RTT_RZQ6			0x42
+#define REG_DDR3_MR1_RTT_RZQ8			0x202
+#define REG_DDR3_MR1_RTT_RZQ12			0x4
+#define REG_DDR3_MR1_OUTBUF_WL_MASK		0xFFFFEF7F	/* WL-disabled,OB-enabled */
+#define REG_DDR3_MR1_OUTBUF_DIS_OFFS		12	/* Output Buffer Disabled */
+#define REG_DDR3_MR1_WL_ENA_OFFS		7
+#define REG_DDR3_MR1_WL_ENA			0x80	/* WL Enabled */
+#define REG_DDR3_MR1_ODT_MASK			0xFFFFFDBB
+
+#define	REG_DDR3_MR2_ADDR			0x15D8
+#define	REG_DDR3_MR2_CS_ADDR			0x1878
+#define	REG_DDR3_MR2_CWL_OFFS			3
+#define	REG_DDR3_MR2_CWL_MASK			0x7
+#define REG_DDR3_MR2_ODT_MASK			0xFFFFF9FF
+#define	REG_DDR3_MR3_ADDR			0x15DC
+#define	REG_DDR3_MR3_CS_ADDR			0x187C
+
+#define REG_DDR3_RANK_CTRL_ADDR			0x15E0
+#define REG_DDR3_RANK_CTRL_CS_ENA_MASK		0xF
+#define REG_DDR3_RANK_CTRL_MIRROR_OFFS		4
+
+#define REG_ZQC_CONF_ADDR			0x15E4
+
+#define REG_DRAM_PHY_CONFIG_ADDR		0x15EC
+#define REG_DRAM_PHY_CONFIG_MASK		0x3FFFFFFF
+
+#define REG_ODPG_CNTRL_ADDR			0x1600
+#define REG_ODPG_CNTRL_OFFS			21
+
+#define REG_PHY_LOCK_MASK_ADDR			0x1670
+#define REG_PHY_LOCK_MASK_MASK			0xFFFFF000
+
+#define REG_PHY_LOCK_STATUS_ADDR		0x1674
+#define REG_PHY_LOCK_STATUS_LOCK_OFFS		9
+#define REG_PHY_LOCK_STATUS_LOCK_MASK		0xFFF
+#define REG_PHY_LOCK_APLL_ADLL_STATUS_MASK	0x7FF
+
+#define REG_PHY_REGISTRY_FILE_ACCESS_ADDR	0x16A0
+#define REG_PHY_REGISTRY_FILE_ACCESS_OP_WR	0xC0000000
+#define REG_PHY_REGISTRY_FILE_ACCESS_OP_RD	0x80000000
+#define REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE	0x80000000
+#define REG_PHY_BC_OFFS				27
+#define REG_PHY_CNTRL_OFFS			26
+#define REG_PHY_CS_OFFS				16
+#define REG_PHY_DQS_REF_DLY_OFFS		10
+#define REG_PHY_PHASE_OFFS			8
+#define REG_PHY_PUP_OFFS			22
+
+#define REG_TRAINING_WL_ADDR			0x16AC
+#define REG_TRAINING_WL_CS_MASK			0xFFFFFFFC
+#define REG_TRAINING_WL_UPD_OFFS		2
+#define REG_TRAINING_WL_CS_DONE_OFFS		3
+#define REG_TRAINING_WL_RATIO_MASK		0xFFFFFF0F
+#define REG_TRAINING_WL_1TO1			0x50
+#define REG_TRAINING_WL_2TO1			0x10
+#define REG_TRAINING_WL_DELAYEXP_MASK		0x20000000
+#define REG_TRAINING_WL_RESULTS_MASK		0x000001FF
+#define REG_TRAINING_WL_RESULTS_OFFS		20
+
+#define REG_REGISTERED_DRAM_CTRL_ADDR		0x16D0
+#define REG_REGISTERED_DRAM_CTRL_SR_FLOAT_OFFS	15
+#define REG_REGISTERED_DRAM_CTRL_PARITY_MASK	0x3F
+/* DLB*/
+#define REG_STATIC_DRAM_DLB_CONTROL		0x1700
+#define DLB_BUS_OPTIMIZATION_WEIGHTS_REG	0x1704
+#define DLB_AGING_REGISTER			0x1708
+#define DLB_EVICTION_CONTROL_REG		0x170c
+#define DLB_EVICTION_TIMERS_REGISTER_REG	0x1710
+
+#define DLB_ENABLE				0x1
+#define DLB_WRITE_COALESING			(0x1 << 2)
+#define DLB_AXI_PREFETCH_EN			(0x1 << 3)
+#define DLB_MBUS_PREFETCH_EN			(0x1 << 4)
+#define PREFETCH_NLNSZTR			(0x1 << 6)
+
+/* CPU    */
+#define REG_BOOTROM_ROUTINE_ADDR		0x182D0
+#define REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS	12
+
+#define REG_DRAM_INIT_CTRL_STATUS_ADDR		0x18488
+#define REG_DRAM_INIT_CTRL_TRN_CLK_OFFS		16
+#define REG_CPU_DIV_CLK_CTRL_0_NEW_RATIO	0x000200FF
+#define REG_DRAM_INIT_CTRL_STATUS_2_ADDR	0x1488
+
+#define REG_CPU_DIV_CLK_CTRL_0_ADDR		0x18700
+
+#define REG_CPU_DIV_CLK_CTRL_1_ADDR		0x18704
+#define REG_CPU_DIV_CLK_CTRL_2_ADDR		0x18708
+
+#define REG_CPU_DIV_CLK_CTRL_3_ADDR		0x1870C
+#define REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK	0xFFFFC0FF
+#define REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS	8
+
+#define REG_CPU_DIV_CLK_CTRL_4_ADDR		0x18710
+
+#define REG_CPU_DIV_CLK_STATUS_0_ADDR		0x18718
+#define REG_CPU_DIV_CLK_ALL_STABLE_OFFS		8
+
+#define REG_CPU_PLL_CTRL_0_ADDR			0x1871C
+#define REG_CPU_PLL_STATUS_0_ADDR		0x18724
+#define REG_CORE_DIV_CLK_CTRL_ADDR		0x18740
+#define REG_CORE_DIV_CLK_STATUS_ADDR		0x18744
+#define REG_DDRPHY_APLL_CTRL_ADDR		0x18780
+
+#define REG_DDRPHY_APLL_CTRL_2_ADDR		0x18784
+
+#define REG_SFABRIC_CLK_CTRL_ADDR		0x20858
+#define REG_SFABRIC_CLK_CTRL_SMPL_OFFS		8
+
+/* DRAM Windows */
+#define REG_XBAR_WIN_19_CTRL_ADDR		0x200e8
+#define REG_XBAR_WIN_4_CTRL_ADDR		0x20040
+#define REG_XBAR_WIN_4_BASE_ADDR		0x20044
+#define REG_XBAR_WIN_4_REMAP_ADDR		0x20048
+#define REG_FASTPATH_WIN_0_CTRL_ADDR		0x20184
+#define REG_XBAR_WIN_7_REMAP_ADDR		0x20078
+
+/* SRAM */
+#define REG_CDI_CONFIG_ADDR			0x20220
+#define REG_SRAM_WINDOW_0_ADDR			0x20240
+#define REG_SRAM_WINDOW_0_ENA_OFFS		0
+#define REG_SRAM_WINDOW_1_ADDR			0x20244
+#define REG_SRAM_L2_ENA_ADDR			0x8500
+#define REG_SRAM_CLEAN_BY_WAY_ADDR		0x87BC
+
+/* PMU */
+#define REG_PMU_I_F_CTRL_ADDR			0x1C090
+#define REG_PMU_DUNIT_BLK_OFFS			16
+#define REG_PMU_DUNIT_RFRS_OFFS			20
+#define REG_PMU_DUNIT_ACK_OFFS			24
+
+/* MBUS*/
+#define MBUS_UNITS_PRIORITY_CONTROL_REG		(MV_MBUS_REGS_OFFSET + 0x420)
+#define FABRIC_UNITS_PRIORITY_CONTROL_REG	(MV_MBUS_REGS_OFFSET + 0x424)
+#define MBUS_UNITS_PREFETCH_CONTROL_REG		(MV_MBUS_REGS_OFFSET + 0x428)
+#define FABRIC_UNITS_PREFETCH_CONTROL_REG	(MV_MBUS_REGS_OFFSET + 0x42c)
+
+#define REG_PM_STAT_MASK_ADDR			0x2210C
+#define REG_PM_STAT_MASK_CPU0_IDLE_MASK_OFFS	16
+
+#define REG_PM_EVENT_STAT_MASK_ADDR		0x22120
+#define REG_PM_EVENT_STAT_MASK_DFS_DONE_OFFS	17
+
+#define REG_PM_CTRL_CONFIG_ADDR			0x22104
+#define REG_PM_CTRL_CONFIG_DFS_REQ_OFFS		18
+
+#define REG_FABRIC_LOCAL_IRQ_MASK_ADDR		0x218C4
+#define REG_FABRIC_LOCAL_IRQ_PMU_MASK_OFFS	18
+
+/* Controller revision info */
+#define PCI_CLASS_CODE_AND_REVISION_ID		0x008
+#define PCCRIR_REVID_OFFS			0	/* Revision ID */
+#define PCCRIR_REVID_MASK			(0xff << PCCRIR_REVID_OFFS)
+
+/*  Power Management Clock Gating Control Register	*/
+#define MV_PEX_IF_REGS_OFFSET(if) \
+	(if < 8 ? (0x40000 + ((if) / 4) * 0x40000 + ((if) % 4) * 0x4000) \
+	 : (0x42000 + ((if) % 8) * 0x40000))
+#define MV_PEX_IF_REGS_BASE(unit)		(MV_PEX_IF_REGS_OFFSET(unit))
+#define POWER_MNG_CTRL_REG			0x18220
+#define PEX_DEVICE_AND_VENDOR_ID		0x000
+#define PEX_CFG_DIRECT_ACCESS(if, reg)		(MV_PEX_IF_REGS_BASE(if) + (reg))
+#define PMC_PEXSTOPCLOCK_OFFS(port)		((port) < 8 ? (5 + (port)) : (18 + (port)))
+#define PMC_PEXSTOPCLOCK_MASK(port)		(1 << PMC_PEXSTOPCLOCK_OFFS(port))
+#define PMC_PEXSTOPCLOCK_EN(port)		(1 << PMC_PEXSTOPCLOCK_OFFS(port))
+#define PMC_PEXSTOPCLOCK_STOP(port)		(0 << PMC_PEXSTOPCLOCK_OFFS(port))
+
+/* TWSI */
+#define TWSI_DATA_ADDR_MASK			0x7
+#define TWSI_DATA_ADDR_OFFS			1
+
+/* General */
+#define MAX_CS					4
+
+/* Frequencies */
+#define FAB_OPT					21
+#define CLK_CPU					12
+#define CLK_VCO					(2 * CLK_CPU)
+#define CLK_DDR					12
+
+/* Cpu Frequencies: */
+#define CLK_CPU_1000				0
+#define CLK_CPU_1066				1
+#define CLK_CPU_1200				2
+#define CLK_CPU_1333				3
+#define CLK_CPU_1500				4
+#define CLK_CPU_1666				5
+#define CLK_CPU_1800				6
+#define CLK_CPU_2000				7
+#define CLK_CPU_600				8
+#define CLK_CPU_667				9
+#define CLK_CPU_800				0xa
+
+/* Extra Cpu Frequencies: */
+#define CLK_CPU_1600				11
+#define CLK_CPU_2133				12
+#define CLK_CPU_2200				13
+#define CLK_CPU_2400				14
+
+/* DDR3 Frequencies: */
+#define DDR_100					0
+#define DDR_300					1
+#define DDR_333					1
+#define DDR_360					2
+#define DDR_400					3
+#define DDR_444					4
+#define DDR_500					5
+#define DDR_533					6
+#define DDR_600					7
+#define DDR_640					8
+#define DDR_666					8
+#define DDR_720					9
+#define DDR_750					9
+#define DDR_800					10
+#define DDR_833					11
+#define DDR_HCLK				20
+#define DDR_S					12
+#define DDR_S_1TO1				13
+#define MARGIN_FREQ				DDR_400
+#define DFS_MARGIN				DDR_100
+
+#define ODT_OPT					16
+#define ODT20					0x200
+#define ODT30					0x204
+#define ODT40					0x44
+#define ODT120					0x40
+#define ODT120D					0x400
+
+#define MRS_DELAY				100
+
+#define SDRAM_WL_SW_OFFS			0x100
+#define SDRAM_RL_OFFS				0x0
+#define SDRAM_PBS_I_OFFS			0x140
+#define SDRAM_PBS_II_OFFS			0x180
+#define SDRAM_PBS_NEXT_OFFS			(SDRAM_PBS_II_OFFS - SDRAM_PBS_I_OFFS)
+#define SDRAM_PBS_TX_OFFS			0x180
+#define SDRAM_PBS_TX_DM_OFFS			576
+#define SDRAM_DQS_RX_OFFS			1024
+#define SDRAM_DQS_TX_OFFS			2048
+#define SDRAM_DQS_RX_SPECIAL_OFFS		5120
+
+#define LEN_STD_PATTERN				16
+#define LEN_KILLER_PATTERN			128
+#define LEN_SPECIAL_PATTERN			128
+#define LEN_PBS_PATTERN				16
+
+#endif /* __DDR3_AXP_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_axp_config.h b/drivers/ddr/marvell/axp/ddr3_axp_config.h
new file mode 100644
index 0000000..10d064d
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_axp_config.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __DDR3_AXP_CONFIG_H
+#define __DDR3_AXP_CONFIG_H
+
+/*
+ * DDR3_LOG_LEVEL Information
+ *
+ * Level 0: Provides an error code in a case of failure, RL, WL errors
+ *          and other algorithm failure
+ * Level 1: Provides the D-Unit setup (SPD/Static configuration)
+ * Level 2: Provides the windows margin as a results of DQS centeralization
+ * Level 3: Provides the windows margin of each DQ as a results of DQS
+ *          centeralization
+ */
+#ifdef CONFIG_DDR_LOG_LEVEL
+#define	DDR3_LOG_LEVEL	CONFIG_DDR_LOG_LEVEL
+#else
+#define	DDR3_LOG_LEVEL	0
+#endif
+
+#define DDR3_PBS        1
+
+/* This flag allows the execution of SW WL/RL upon HW failure */
+#define DDR3_RUN_SW_WHEN_HW_FAIL    1
+
+/*
+ * General Configurations
+ *
+ * The following parameters are required for proper setup:
+ *
+ * DDR_TARGET_FABRIC   - Set desired fabric configuration
+ *                       (for sample@Reset fabfreq parameter)
+ * DRAM_ECC            - Set ECC support 1/0
+ * BUS_WIDTH           - 64/32 bit
+ * CONFIG_SPD_EEPROM   - Enables auto detection of DIMMs and their timing values
+ * DQS_CLK_ALIGNED     - Set this if CLK and DQS signals are aligned on board
+ * MIXED_DIMM_STATIC   - Mixed DIMM + On board devices support (ODT registers
+ *                       values are taken statically)
+ * DDR3_TRAINING_DEBUG - Debug prints of internal code
+ */
+#define DDR_TARGET_FABRIC			5
+/* Only enable ECC if the board selects it */
+#ifdef CONFIG_BOARD_ECC_SUPPORT
+#define DRAM_ECC				1
+#else
+#define DRAM_ECC				0
+#endif
+
+#ifdef CONFIG_DDR_32BIT
+#define BUS_WIDTH                               32
+#else
+#define BUS_WIDTH				64
+#endif
+
+#undef DQS_CLK_ALIGNED
+#undef MIXED_DIMM_STATIC
+#define DDR3_TRAINING_DEBUG			0
+#define REG_DIMM_SKIP_WL			0
+
+/* Marvell boards specific configurations */
+#if defined(DB_78X60_PCAC)
+#undef CONFIG_SPD_EEPROM
+#define STATIC_TRAINING
+#endif
+
+#if defined(DB_78X60_AMC)
+#undef CONFIG_SPD_EEPROM
+#undef  DRAM_ECC
+#define DRAM_ECC				1
+#endif
+
+#ifdef CONFIG_SPD_EEPROM
+/*
+ * DIMM support parameters:
+ * DRAM_2T - Set Desired 2T Mode - 0 - 1T, 0x1 - 2T, 0x2 - 3T
+ * DIMM_CS_BITMAP - bitmap representing the optional CS in DIMMs
+ * (0xF=CS0+CS1+CS2+CS3, 0xC=CS2+CS3...)
+ */
+#define DRAM_2T					0x0
+#define DIMM_CS_BITMAP				0xF
+#define DUNIT_SPD
+#endif
+
+#ifdef DRAM_ECC
+/*
+ * ECC support parameters:
+ *
+ * U_BOOT_START_ADDR, U_BOOT_SCRUB_SIZE - relevant when using ECC and need
+ * to configure the scrubbing area
+ */
+#define TRAINING_SIZE				0x20000
+#define U_BOOT_START_ADDR			0
+#define U_BOOT_SCRUB_SIZE			0x1000000 /* TRAINING_SIZE */
+#endif
+
+/*
+ * Registered DIMM Support - In case registered DIMM is attached,
+ * please supply the following values:
+ * (see JEDEC - JESD82-29A "Definition of the SSTE32882 Registering Clock
+ * Driver with Parity and Quad Chip
+ * Selects for DDR3/DDR3L/DDR3U RDIMM 1.5 V/1.35 V/1.25 V Applications")
+ * RC0: Global Features Control Word
+ * RC1: Clock Driver Enable Control Word
+ * RC2: Timing Control Word
+ * RC3-RC5 - taken from SPD
+ * RC8: Additional IBT Setting Control Word
+ * RC9: Power Saving Settings Control Word
+ * RC10: Encoding for RDIMM Operating Speed
+ * RC11: Operating Voltage VDD and VREFCA Control Word
+ */
+#define RDIMM_RC0				0
+#define RDIMM_RC1				0
+#define RDIMM_RC2				0
+#define RDIMM_RC8				0
+#define RDIMM_RC9				0
+#define RDIMM_RC10				0x2
+#define RDIMM_RC11				0x0
+
+#if defined(MIXED_DIMM_STATIC) || !defined(CONFIG_SPD_EEPROM)
+#define DUNIT_STATIC
+#endif
+
+#if defined(MIXED_DIMM_STATIC) || defined(CONFIG_SPD_EEPROM)
+/*
+ * This flag allows the user to change the dram refresh cycle in ps,
+ * only in case of SPD or MIX DIMM topology
+ */
+#define TREFI_USER_EN
+
+#ifdef TREFI_USER_EN
+#define TREFI_USER				3900000
+#endif
+#endif
+
+#ifdef CONFIG_SPD_EEPROM
+/*
+ * AUTO_DETECTION_SUPPORT - relevant ONLY for Marvell DB boards.
+ * Enables I2C auto detection different options
+ */
+#if defined(CONFIG_DB_88F78X60) || defined(CONFIG_DB_88F78X60_REV2) || \
+    defined(CONFIG_DB_784MP_GP)
+#define AUTO_DETECTION_SUPPORT
+#endif
+#endif
+
+#endif /* __DDR3_AXP_CONFIG_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_axp_mc_static.h b/drivers/ddr/marvell/axp/ddr3_axp_mc_static.h
new file mode 100644
index 0000000..5d43ae5
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_axp_mc_static.h
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __AXP_MC_STATIC_H
+#define __AXP_MC_STATIC_H
+
+MV_DRAM_MC_INIT ddr3_A0_db_667[MV_MAX_DDR3_STATIC_SIZE] = {
+#ifdef CONFIG_DDR_32BIT
+	{0x00001400, 0x7301c924},	/*DDR SDRAM Configuration Register */
+#else /*CONFIG_DDR_64BIT */
+	{0x00001400, 0x7301CA28},	/*DDR SDRAM Configuration Register */
+#endif
+	{0x00001404, 0x3630b800},	/*Dunit Control Low Register */
+	{0x00001408, 0x43149775},	/*DDR SDRAM Timing (Low) Register */
+	/* {0x0000140C, 0x38000C6A}, *//*DDR SDRAM Timing (High) Register */
+	{0x0000140C, 0x38d83fe0},	/*DDR SDRAM Timing (High) Register */
+
+#ifdef DB_78X60_PCAC
+	{0x00001410, 0x040F0001},	/*DDR SDRAM Address Control Register */
+#else
+	{0x00001410, 0x040F0000},	/*DDR SDRAM Open Pages Control Register */
+#endif
+
+	{0x00001414, 0x00000000},	/*DDR SDRAM Open Pages Control Register */
+	{0x00001418, 0x00000e00},	/*DDR SDRAM Operation Register */
+	{0x00001420, 0x00000004},	/*DDR SDRAM Extended Mode Register */
+	{0x00001424, 0x0000D3FF},	/*Dunit Control High Register */
+	{0x00001428, 0x000F8830},	/*Dunit Control High Register */
+	{0x0000142C, 0x214C2F38},	/*Dunit Control High Register */
+	{0x0000147C, 0x0000c671},
+
+	{0x000014a0, 0x000002A9},
+	{0x000014a8, 0x00000101},	/*2:1 */
+	{0x00020220, 0x00000007},
+
+	{0x00001494, 0x00010000},	/*DDR SDRAM ODT Control (Low) Register */
+	{0x00001498, 0x00000000},	/*DDR SDRAM ODT Control (High) Register */
+	{0x0000149C, 0x00000301},	/*DDR Dunit ODT Control Register */
+
+	{0x000014C0, 0x192434e9},	/* DRAM address and Control Driving Strenght  */
+	{0x000014C4, 0x092434e9},	/* DRAM Data and DQS Driving Strenght  */
+
+	{0x000200e8, 0x3FFF0E01},	/* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */
+	{0x00020184, 0x3FFFFFE0},	/* DO NOT Modify - Close fast path Window to - 2G */
+
+	{0x0001504, 0x7FFFFFF1},	/* CS0 Size */
+	{0x000150C, 0x00000000},	/* CS1 Size */
+	{0x0001514, 0x00000000},	/* CS2 Size */
+	{0x000151C, 0x00000000},	/* CS3 Size */
+
+	/*     {0x00001524, 0x0000C800},  */
+	{0x00001538, 0x0000000b},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000d},	/*Read Data Ready Delay Register */
+
+	{0x000015D0, 0x00000640},	/*MR0 */
+	{0x000015D4, 0x00000046},	/*MR1 */
+	{0x000015D8, 0x00000010},	/*MR2 */
+	{0x000015DC, 0x00000000},	/*MR3 */
+
+	{0x000015E4, 0x00203c18},	/*ZQC Configuration Register */
+	{0x000015EC, 0xd800aa25},	/*DDR PHY */
+	{0x0, 0x0}
+};
+
+MV_DRAM_MC_INIT ddr3_A0_AMC_667[MV_MAX_DDR3_STATIC_SIZE] = {
+#ifdef CONFIG_DDR_32BIT
+	{0x00001400, 0x7301c924},	/*DDR SDRAM Configuration Register */
+#else /*CONFIG_DDR_64BIT */
+	{0x00001400, 0x7301CA28},	/*DDR SDRAM Configuration Register */
+#endif
+	{0x00001404, 0x3630b800},	/*Dunit Control Low Register */
+	{0x00001408, 0x43149775},	/*DDR SDRAM Timing (Low) Register */
+	/* {0x0000140C, 0x38000C6A}, *//*DDR SDRAM Timing (High) Register */
+	{0x0000140C, 0x38d83fe0},	/*DDR SDRAM Timing (High) Register */
+
+#ifdef DB_78X60_PCAC
+	{0x00001410, 0x040F0001},	/*DDR SDRAM Address Control Register */
+#else
+	{0x00001410, 0x040F000C},	/*DDR SDRAM Open Pages Control Register */
+#endif
+
+	{0x00001414, 0x00000000},	/*DDR SDRAM Open Pages Control Register */
+	{0x00001418, 0x00000e00},	/*DDR SDRAM Operation Register */
+	{0x00001420, 0x00000004},	/*DDR SDRAM Extended Mode Register */
+	{0x00001424, 0x0000D3FF},	/*Dunit Control High Register */
+	{0x00001428, 0x000F8830},	/*Dunit Control High Register */
+	{0x0000142C, 0x214C2F38},	/*Dunit Control High Register */
+	{0x0000147C, 0x0000c671},
+
+	{0x000014a0, 0x000002A9},
+	{0x000014a8, 0x00000101},	/*2:1 */
+	{0x00020220, 0x00000007},
+
+	{0x00001494, 0x00010000},	/*DDR SDRAM ODT Control (Low) Register */
+	{0x00001498, 0x00000000},	/*DDR SDRAM ODT Control (High) Register */
+	{0x0000149C, 0x00000301},	/*DDR Dunit ODT Control Register */
+
+	{0x000014C0, 0x192434e9},	/* DRAM address and Control Driving Strenght  */
+	{0x000014C4, 0x092434e9},	/* DRAM Data and DQS Driving Strenght  */
+
+	{0x000200e8, 0x3FFF0E01},	/* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */
+	{0x00020184, 0x3FFFFFE0},	/* DO NOT Modify - Close fast path Window to - 2G */
+
+	{0x0001504, 0x3FFFFFF1},	/* CS0 Size */
+	{0x000150C, 0x00000000},	/* CS1 Size */
+	{0x0001514, 0x00000000},	/* CS2 Size */
+	{0x000151C, 0x00000000},	/* CS3 Size */
+
+	/*     {0x00001524, 0x0000C800},  */
+	{0x00001538, 0x0000000b},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000d},	/*Read Data Ready Delay Register */
+
+	{0x000015D0, 0x00000640},	/*MR0 */
+	{0x000015D4, 0x00000046},	/*MR1 */
+	{0x000015D8, 0x00000010},	/*MR2 */
+	{0x000015DC, 0x00000000},	/*MR3 */
+
+	{0x000015E4, 0x00203c18},	/*ZQC Configuration Register */
+	{0x000015EC, 0xd800aa25},	/*DDR PHY */
+	{0x0, 0x0}
+};
+
+MV_DRAM_MC_INIT ddr3_A0_db_400[MV_MAX_DDR3_STATIC_SIZE] = {
+#ifdef CONFIG_DDR_32BIT
+	{0x00001400, 0x73004C30},	/*DDR SDRAM Configuration Register */
+#else /* CONFIG_DDR_64BIT */
+	{0x00001400, 0x7300CC30},	/*DDR SDRAM Configuration Register */
+#endif
+	{0x00001404, 0x3630B840},	/*Dunit Control Low Register */
+	{0x00001408, 0x33137663},	/*DDR SDRAM Timing (Low) Register */
+	{0x0000140C, 0x38000C55},	/*DDR SDRAM Timing (High) Register */
+	{0x00001410, 0x040F0000},	/*DDR SDRAM Address Control Register */
+	{0x00001414, 0x00000000},	/*DDR SDRAM Open Pages Control Register */
+	{0x00001418, 0x00000e00},	/*DDR SDRAM Operation Register */
+	{0x0000141C, 0x00000672},	/*DDR SDRAM Mode Register */
+	{0x00001420, 0x00000004},	/*DDR SDRAM Extended Mode Register */
+	{0x00001424, 0x0100D3FF},	/*Dunit Control High Register */
+	{0x00001428, 0x000D6720},	/*Dunit Control High Register */
+	{0x0000142C, 0x014C2F38},	/*Dunit Control High Register */
+	{0x0000147C, 0x00006571},
+
+	{0x00001494, 0x00010000},	/*DDR SDRAM ODT Control (Low) Register */
+	{0x00001498, 0x00000000},	/*DDR SDRAM ODT Control (High) Register */
+	{0x0000149C, 0x00000301},	/*DDR Dunit ODT Control Register */
+
+	{0x000014a0, 0x000002A9},
+	{0x000014a8, 0x00000101},	/*2:1 */
+	{0x00020220, 0x00000007},
+
+	{0x000014C0, 0x192424C8},	/* DRAM address and Control Driving Strenght  */
+	{0x000014C4, 0xEFB24C8},	/* DRAM Data and DQS Driving Strenght  */
+
+	{0x000200e8, 0x3FFF0E01},	/* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */
+	{0x00020184, 0x3FFFFFE0},	/* DO NOT Modify - Close fast path Window to - 2G */
+
+	{0x0001504, 0x7FFFFFF1},	/* CS0 Size */
+	{0x000150C, 0x00000000},	/* CS1 Size */
+	{0x0001514, 0x00000000},	/* CS2 Size */
+	{0x000151C, 0x00000000},	/* CS3 Size */
+
+	{0x00001538, 0x00000008},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000A},	/*Read Data Ready Delay Register */
+
+	{0x000015D0, 0x00000630},	/*MR0 */
+	{0x000015D4, 0x00000046},	/*MR1 */
+	{0x000015D8, 0x00000008},	/*MR2 */
+	{0x000015DC, 0x00000000},	/*MR3 */
+
+	{0x000015E4, 0x00203c18},	/*ZQDS Configuration Register */
+	/* {0x000015EC, 0xDE000025}, *//*DDR PHY */
+	{0x000015EC, 0xF800AA25},	/*DDR PHY */
+	{0x0, 0x0}
+};
+
+MV_DRAM_MC_INIT ddr3_Z1_db_600[MV_MAX_DDR3_STATIC_SIZE] = {
+#ifdef CONFIG_DDR_32BIT
+	{0x00001400, 0x73014A28},	/*DDR SDRAM Configuration Register */
+#else /*CONFIG_DDR_64BIT */
+	{0x00001400, 0x7301CA28},	/*DDR SDRAM Configuration Register */
+#endif
+	{0x00001404, 0x3630B040},	/*Dunit Control Low Register */
+	{0x00001408, 0x44149887},	/*DDR SDRAM Timing (Low) Register */
+	/* {0x0000140C, 0x38000C6A}, *//*DDR SDRAM Timing (High) Register */
+	{0x0000140C, 0x38D83FE0},	/*DDR SDRAM Timing (High) Register */
+
+#ifdef DB_78X60_PCAC
+	{0x00001410, 0x040F0001},	/*DDR SDRAM Address Control Register */
+#else
+	{0x00001410, 0x040F0000},	/*DDR SDRAM Open Pages Control Register */
+#endif
+
+	{0x00001414, 0x00000000},	/*DDR SDRAM Open Pages Control Register */
+	{0x00001418, 0x00000e00},	/*DDR SDRAM Operation Register */
+	{0x00001420, 0x00000004},	/*DDR SDRAM Extended Mode Register */
+	{0x00001424, 0x0100D1FF},	/*Dunit Control High Register */
+	{0x00001428, 0x000F8830},	/*Dunit Control High Register */
+	{0x0000142C, 0x214C2F38},	/*Dunit Control High Register */
+	{0x0000147C, 0x0000c671},
+
+	{0x000014a8, 0x00000101},	/*2:1 */
+	{0x00020220, 0x00000007},
+
+	{0x00001494, 0x00010000},	/*DDR SDRAM ODT Control (Low) Register */
+	{0x00001498, 0x00000000},	/*DDR SDRAM ODT Control (High) Register */
+	{0x0000149C, 0x00000301},	/*DDR Dunit ODT Control Register */
+
+	{0x000014C0, 0x192424C8},	/* DRAM address and Control Driving Strenght  */
+	{0x000014C4, 0xEFB24C8},	/* DRAM Data and DQS Driving Strenght  */
+
+	{0x000200e8, 0x3FFF0E01},	/* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */
+	{0x00020184, 0x3FFFFFE0},	/* DO NOT Modify - Close fast path Window to - 2G */
+
+	{0x0001504, 0x7FFFFFF1},	/* CS0 Size */
+	{0x000150C, 0x00000000},	/* CS1 Size */
+	{0x0001514, 0x00000000},	/* CS2 Size */
+	{0x000151C, 0x00000000},	/* CS3 Size */
+
+	/*     {0x00001524, 0x0000C800},  */
+	{0x00001538, 0x0000000b},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000d},	/*Read Data Ready Delay Register */
+
+	{0x000015D0, 0x00000650},	/*MR0 */
+	{0x000015D4, 0x00000046},	/*MR1 */
+	{0x000015D8, 0x00000010},	/*MR2 */
+	{0x000015DC, 0x00000000},	/*MR3 */
+
+	{0x000015E4, 0x00203c18},	/*ZQC Configuration Register */
+	{0x000015EC, 0xDE000025},	/*DDR PHY */
+	{0x0, 0x0}
+};
+
+MV_DRAM_MC_INIT ddr3_Z1_db_300[MV_MAX_DDR3_STATIC_SIZE] = {
+#ifdef CONFIG_DDR_32BIT
+	{0x00001400, 0x73004C30},	/*DDR SDRAM Configuration Register */
+#else /*CONFIG_DDR_64BIT */
+	{0x00001400, 0x7300CC30},	/*DDR SDRAM Configuration Register */
+	/*{0x00001400, 0x7304CC30},  *//*DDR SDRAM Configuration Register */
+#endif
+	{0x00001404, 0x3630B840},	/*Dunit Control Low Register */
+	{0x00001408, 0x33137663},	/*DDR SDRAM Timing (Low) Register */
+	{0x0000140C, 0x38000C55},	/*DDR SDRAM Timing (High) Register */
+	{0x00001410, 0x040F0000},	/*DDR SDRAM Address Control Register */
+	{0x00001414, 0x00000000},	/*DDR SDRAM Open Pages Control Register */
+	{0x00001418, 0x00000e00},	/*DDR SDRAM Operation Register */
+	{0x0000141C, 0x00000672},	/*DDR SDRAM Mode Register */
+	{0x00001420, 0x00000004},	/*DDR SDRAM Extended Mode Register */
+	{0x00001424, 0x0100F1FF},	/*Dunit Control High Register */
+	{0x00001428, 0x000D6720},	/*Dunit Control High Register */
+	{0x0000142C, 0x014C2F38},	/*Dunit Control High Register */
+	{0x0000147C, 0x00006571},
+
+	{0x00001494, 0x00010000},	/*DDR SDRAM ODT Control (Low) Register */
+	{0x00001498, 0x00000000},	/*DDR SDRAM ODT Control (High) Register */
+	{0x0000149C, 0x00000301},	/*DDR Dunit ODT Control Register */
+
+	{0x000014C0, 0x192424C8},	/* DRAM address and Control Driving Strenght  */
+	{0x000014C4, 0xEFB24C8},	/* DRAM Data and DQS Driving Strenght  */
+
+	{0x000200e8, 0x3FFF0E01},	/* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */
+	{0x00020184, 0x3FFFFFE0},	/* DO NOT Modify - Close fast path Window to - 2G */
+
+	{0x0001504, 0x7FFFFFF1},	/* CS0 Size */
+	{0x000150C, 0x00000000},	/* CS1 Size */
+	{0x0001514, 0x00000000},	/* CS2 Size */
+	{0x000151C, 0x00000000},	/* CS3 Size */
+
+	{0x00001538, 0x00000008},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000A},	/*Read Data Ready Delay Register */
+
+	{0x000015D0, 0x00000630},	/*MR0 */
+	{0x000015D4, 0x00000046},	/*MR1 */
+	{0x000015D8, 0x00000008},	/*MR2 */
+	{0x000015DC, 0x00000000},	/*MR3 */
+
+	{0x000015E4, 0x00203c18},	/*ZQDS Configuration Register */
+	{0x000015EC, 0xDE000025},	/*DDR PHY */
+
+	{0x0, 0x0}
+};
+
+#endif /* __AXP_MC_STATIC_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_axp_training_static.h b/drivers/ddr/marvell/axp/ddr3_axp_training_static.h
new file mode 100644
index 0000000..253c11d
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_axp_training_static.h
@@ -0,0 +1,769 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __AXP_TRAINING_STATIC_H
+#define __AXP_TRAINING_STATIC_H
+
+/*
+ * STATIC_TRAINING - Set only if static parameters for training are set and
+ * required
+ */
+
+MV_DRAM_TRAINING_INIT ddr3_db_rev2_667[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0     */
+	{0x000016A0, 0xC002011A},
+	/*1 */
+	{0x000016A0, 0xC0420100},
+	/*2 */
+	{0x000016A0, 0xC082020A},
+	/*3 */
+	{0x000016A0, 0xC0C20017},
+	/*4 */
+	{0x000016A0, 0xC1020113},
+	/*5 */
+	{0x000016A0, 0xC1420107},
+	/*6 */
+	{0x000016A0, 0xC182011F},
+	/*7 */
+	{0x000016A0, 0xC1C2001C},
+	/*8 */
+	{0x000016A0, 0xC202010D},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0004A06},
+	/*1 */
+	{0x000016A0, 0xC040690D},
+	/*2 */
+	{0x000016A0, 0xC0806A0D},
+	/*3 */
+	{0x000016A0, 0xC0C0A01B},
+	/*4 */
+	{0x000016A0, 0xC1003A01},
+	/*5 */
+	{0x000016A0, 0xC1408113},
+	/*6 */
+	{0x000016A0, 0xC1805609},
+	/*7 */
+	{0x000016A0, 0xC1C04504},
+	/*8 */
+	{0x000016A0, 0xC2009518},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_rev2_800[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0     */
+	{0x000016A0, 0xC0020301},
+	/*1 */
+	{0x000016A0, 0xC0420202},
+	/*2 */
+	{0x000016A0, 0xC0820314},
+	/*3 */
+	{0x000016A0, 0xC0C20117},
+	/*4 */
+	{0x000016A0, 0xC1020219},
+	/*5 */
+	{0x000016A0, 0xC142020B},
+	/*6 */
+	{0x000016A0, 0xC182030A},
+	/*7 */
+	{0x000016A0, 0xC1C2011D},
+	/*8 */
+	{0x000016A0, 0xC2020212},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0007A12},
+	/*1 */
+	{0x000016A0, 0xC0408D16},
+	/*2 */
+	{0x000016A0, 0xC0809E1B},
+	/*3 */
+	{0x000016A0, 0xC0C0AC1F},
+	/*4 */
+	{0x000016A0, 0xC1005E0A},
+	/*5 */
+	{0x000016A0, 0xC140A91D},
+	/*6 */
+	{0x000016A0, 0xC1808E17},
+	/*7 */
+	{0x000016A0, 0xC1C05509},
+	/*8 */
+	{0x000016A0, 0xC2003A01},
+
+	/* PBS Leveling */
+	/*0 */
+	{0x000016A0, 0xC0007A12},
+	/*1 */
+	{0x000016A0, 0xC0408D16},
+	/*2 */
+	{0x000016A0, 0xC0809E1B},
+	/*3 */
+	{0x000016A0, 0xC0C0AC1F},
+	/*4 */
+	{0x000016A0, 0xC1005E0A},
+	/*5 */
+	{0x000016A0, 0xC140A91D},
+	/*6 */
+	{0x000016A0, 0xC1808E17},
+	/*7 */
+	{0x000016A0, 0xC1C05509},
+	/*8 */
+	{0x000016A0, 0xC2003A01},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000B},
+
+	{0x00001538, 0x0000000D},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x00000011},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_400[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0             2               4               15 */
+	{0x000016A0, 0xC002010C},
+	/*1             2               4               2 */
+	{0x000016A0, 0xC042001C},
+	/*2             2               4               27 */
+	{0x000016A0, 0xC0820115},
+	/*3             2               4               0 */
+	{0x000016A0, 0xC0C20019},
+	/*4             2               4               13 */
+	{0x000016A0, 0xC1020108},
+	/*5             2               4               5 */
+	{0x000016A0, 0xC1420100},
+	/*6             2               4               19 */
+	{0x000016A0, 0xC1820111},
+	/*7             2               4               0 */
+	{0x000016A0, 0xC1C2001B},
+	/*8             2               4               10 */
+	/*{0x000016A0, 0xC2020117}, */
+	{0x000016A0, 0xC202010C},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0005508},
+	/*1 */
+	{0x000016A0, 0xC0409819},
+	/*2 */
+	{0x000016A0, 0xC080650C},
+	/*3 */
+	{0x000016A0, 0xC0C0700F},
+	/*4 */
+	{0x000016A0, 0xC1004103},
+	/*5 */
+	{0x000016A0, 0xC140A81D},
+	/*6 */
+	{0x000016A0, 0xC180650C},
+	/*7 */
+	{0x000016A0, 0xC1C08013},
+	/*8 */
+	{0x000016A0, 0xC2005508},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x00000008},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000A},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_533[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0             2               4               15 */
+	{0x000016A0, 0xC002040C},
+	/*1             2               4               2 */
+	{0x000016A0, 0xC0420117},
+	/*2             2               4               27 */
+	{0x000016A0, 0xC082041B},
+	/*3             2               4               0 */
+	{0x000016A0, 0xC0C20117},
+	/*4             2               4               13 */
+	{0x000016A0, 0xC102040A},
+	/*5             2               4               5 */
+	{0x000016A0, 0xC1420117},
+	/*6             2               4               19 */
+	{0x000016A0, 0xC1820419},
+	/*7             2               4               0 */
+	{0x000016A0, 0xC1C20117},
+	/*8             2               4               10 */
+	{0x000016A0, 0xC2020117},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0008113},
+	/*1 */
+	{0x000016A0, 0xC0404504},
+	/*2 */
+	{0x000016A0, 0xC0808514},
+	/*3 */
+	{0x000016A0, 0xC0C09418},
+	/*4 */
+	{0x000016A0, 0xC1006D0E},
+	/*5 */
+	{0x000016A0, 0xC1405508},
+	/*6 */
+	{0x000016A0, 0xC1807D12},
+	/*7 */
+	{0x000016A0, 0xC1C0b01F},
+	/*8 */
+	{0x000016A0, 0xC2005D0A},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x00000008},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000A},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_600[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0             2               3               1 */
+	{0x000016A0, 0xC0020104},
+	/*1             2               2               6 */
+	{0x000016A0, 0xC0420010},
+	/*2             2               3               16 */
+	{0x000016A0, 0xC0820112},
+	/*3             2               1               26 */
+	{0x000016A0, 0xC0C20009},
+	/*4             2               2               29 */
+	{0x000016A0, 0xC102001F},
+	/*5             2               2               13 */
+	{0x000016A0, 0xC1420014},
+	/*6             2               3               6 */
+	{0x000016A0, 0xC1820109},
+	/*7             2               1               31 */
+	{0x000016A0, 0xC1C2000C},
+	/*8             2               2               22 */
+	{0x000016A0, 0xC2020112},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0009919},
+	/*1 */
+	{0x000016A0, 0xC0405508},
+	/*2 */
+	{0x000016A0, 0xC0809919},
+	/*3 */
+	{0x000016A0, 0xC0C09C1A},
+	/*4 */
+	{0x000016A0, 0xC1008113},
+	/*5 */
+	{0x000016A0, 0xC140650C},
+	/*6 */
+	{0x000016A0, 0xC1809518},
+	/*7 */
+	{0x000016A0, 0xC1C04103},
+	/*8 */
+	{0x000016A0, 0xC2006D0E},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_667[MV_MAX_DDR3_STATIC_SIZE] = {
+
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0             2               3               1 */
+	{0x000016A0, 0xC0020103},
+	/*1            2               2               6 */
+	{0x000016A0, 0xC0420012},
+	/*2            2               3               16 */
+	{0x000016A0, 0xC0820113},
+	/*3            2               1               26 */
+	{0x000016A0, 0xC0C20012},
+	/*4            2               2               29 */
+	{0x000016A0, 0xC1020100},
+	/*5            2               2               13 */
+	{0x000016A0, 0xC1420016},
+	/*6            2               3               6 */
+	{0x000016A0, 0xC1820109},
+	/*7            2               1               31 */
+	{0x000016A0, 0xC1C20010},
+	/*8            2               2               22 */
+	{0x000016A0, 0xC2020112},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC000b11F},
+	/*1 */
+	{0x000016A0, 0xC040690D},
+	/*2 */
+	{0x000016A0, 0xC0803600},
+	/*3 */
+	{0x000016A0, 0xC0C0a81D},
+	/*4 */
+	{0x000016A0, 0xC1009919},
+	/*5 */
+	{0x000016A0, 0xC1407911},
+	/*6 */
+	{0x000016A0, 0xC180ad1e},
+	/*7 */
+	{0x000016A0, 0xC1C04d06},
+	/*8 */
+	{0x000016A0, 0xC2008514},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_800[MV_MAX_DDR3_STATIC_SIZE] = {
+
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0             2               3               1 */
+	{0x000016A0, 0xC0020213},
+	/*1            2               2               6 */
+	{0x000016A0, 0xC0420108},
+	/*2            2               3               16 */
+	{0x000016A0, 0xC0820210},
+	/*3            2               1               26 */
+	{0x000016A0, 0xC0C20108},
+	/*4            2               2               29 */
+	{0x000016A0, 0xC102011A},
+	/*5            2               2               13 */
+	{0x000016A0, 0xC1420300},
+	/*6            2               3               6 */
+	{0x000016A0, 0xC1820204},
+	/*7            2               1               31 */
+	{0x000016A0, 0xC1C20106},
+	/*8            2               2               22 */
+	{0x000016A0, 0xC2020112},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC000620B},
+	/*1 */
+	{0x000016A0, 0xC0408D16},
+	/*2 */
+	{0x000016A0, 0xC0806A0D},
+	/*3 */
+	{0x000016A0, 0xC0C03D02},
+	/*4 */
+	{0x000016A0, 0xC1004a05},
+	/*5 */
+	{0x000016A0, 0xC140A11B},
+	/*6 */
+	{0x000016A0, 0xC1805E0A},
+	/*7 */
+	{0x000016A0, 0xC1C06D0E},
+	/*8 */
+	{0x000016A0, 0xC200AD1E},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000C},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000E},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_rd_667_0[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0 */
+	{0x000016A0, 0xC002010E},
+	/*1 */
+	{0x000016A0, 0xC042001E},
+	/*2 */
+	{0x000016A0, 0xC0820118},
+	/*3 */
+	{0x000016A0, 0xC0C2001E},
+	/*4 */
+	{0x000016A0, 0xC102010C},
+	/*5 */
+	{0x000016A0, 0xC1420102},
+	/*6 */
+	{0x000016A0, 0xC1820111},
+	/*7 */
+	{0x000016A0, 0xC1C2001C},
+	/*8 */
+	{0x000016A0, 0xC2020109},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0003600},
+	/*1 */
+	{0x000016A0, 0xC040690D},
+	/*2 */
+	{0x000016A0, 0xC0805207},
+	/*3 */
+	{0x000016A0, 0xC0C0A81D},
+	/*4 */
+	{0x000016A0, 0xC1009919},
+	/*5 */
+	{0x000016A0, 0xC1407911},
+	/*6 */
+	{0x000016A0, 0xC1803E02},
+	/*7 */
+	{0x000016A0, 0xC1C05107},
+	/*8 */
+	{0x000016A0, 0xC2008113},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_rd_667_1[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0 */
+	{0x000016A0, 0xC0020106},
+	/*1 */
+	{0x000016A0, 0xC0420016},
+	/*2 */
+	{0x000016A0, 0xC0820117},
+	/*3 */
+	{0x000016A0, 0xC0C2000F},
+	/*4 */
+	{0x000016A0, 0xC1020105},
+	/*5 */
+	{0x000016A0, 0xC142001B},
+	/*6 */
+	{0x000016A0, 0xC182010C},
+	/*7 */
+	{0x000016A0, 0xC1C20011},
+	/*8 */
+	{0x000016A0, 0xC2020101},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0003600},
+	/*1 */
+	{0x000016A0, 0xC0406D0E},
+	/*2 */
+	{0x000016A0, 0xC0803600},
+	/*3 */
+	{0x000016A0, 0xC0C04504},
+	/*4 */
+	{0x000016A0, 0xC1009919},
+	/*5 */
+	{0x000016A0, 0xC1407911},
+	/*6 */
+	{0x000016A0, 0xC1803600},
+	/*7 */
+	{0x000016A0, 0xC1C0610B},
+	/*8 */
+	{0x000016A0, 0xC2008113},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_rd_667_2[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0 */
+	{0x000016A0, 0xC002010C},
+	/*1 */
+	{0x000016A0, 0xC042001B},
+	/*2 */
+	{0x000016A0, 0xC082011D},
+	/*3 */
+	{0x000016A0, 0xC0C20015},
+	/*4 */
+	{0x000016A0, 0xC102010B},
+	/*5 */
+	{0x000016A0, 0xC1420101},
+	/*6 */
+	{0x000016A0, 0xC1820113},
+	/*7 */
+	{0x000016A0, 0xC1C20017},
+	/*8 */
+	{0x000016A0, 0xC2020107},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0003600},
+	/*1 */
+	{0x000016A0, 0xC0406D0E},
+	/*2 */
+	{0x000016A0, 0xC0803600},
+	/*3 */
+	{0x000016A0, 0xC0C04504},
+	/*4 */
+	{0x000016A0, 0xC1009919},
+	/*5 */
+	{0x000016A0, 0xC1407911},
+	/*6 */
+	{0x000016A0, 0xC180B11F},
+	/*7 */
+	{0x000016A0, 0xC1C0610B},
+	/*8 */
+	{0x000016A0, 0xC2008113},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_db_667_M[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/* CS 0 */
+	/*0             2               3               1 */
+	{0x000016A0, 0xC0020103},
+	/*1            2               2               6 */
+	{0x000016A0, 0xC0420012},
+	/*2            2               3               16 */
+	{0x000016A0, 0xC0820113},
+	/*3            2               1               26 */
+	{0x000016A0, 0xC0C20012},
+	/*4            2               2               29 */
+	{0x000016A0, 0xC1020100},
+	/*5            2               2               13 */
+	{0x000016A0, 0xC1420016},
+	/*6            2               3               6 */
+	{0x000016A0, 0xC1820109},
+	/*7            2               1               31 */
+	{0x000016A0, 0xC1C20010},
+	/*8            2               2               22 */
+	{0x000016A0, 0xC2020112},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC000b11F},
+	/*1 */
+	{0x000016A0, 0xC040690D},
+	/*2 */
+	{0x000016A0, 0xC0803600},
+	/*3 */
+	{0x000016A0, 0xC0C0a81D},
+	/*4 */
+	{0x000016A0, 0xC1009919},
+	/*5 */
+	{0x000016A0, 0xC1407911},
+	/*6 */
+	{0x000016A0, 0xC180ad1e},
+	/*7 */
+	{0x000016A0, 0xC1C04d06},
+	/*8 */
+	{0x000016A0, 0xC2008514},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	/* CS 1 */
+
+	{0x000016A0, 0xC0060103},
+	/*1            2               2               6 */
+	{0x000016A0, 0xC0460012},
+	/*2            2               3               16 */
+	{0x000016A0, 0xC0860113},
+	/*3            2               1               26 */
+	{0x000016A0, 0xC0C60012},
+	/*4            2               2               29 */
+	{0x000016A0, 0xC1060100},
+	/*5            2               2               13 */
+	{0x000016A0, 0xC1460016},
+	/*6            2               3               6 */
+	{0x000016A0, 0xC1860109},
+	/*7            2               1               31 */
+	{0x000016A0, 0xC1C60010},
+	/*8            2               2               22 */
+	{0x000016A0, 0xC2060112},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC004b11F},
+	/*1 */
+	{0x000016A0, 0xC044690D},
+	/*2 */
+	{0x000016A0, 0xC0843600},
+	/*3 */
+	{0x000016A0, 0xC0C4a81D},
+	/*4 */
+	{0x000016A0, 0xC1049919},
+	/*5 */
+	{0x000016A0, 0xC1447911},
+	/*6 */
+	{0x000016A0, 0xC184ad1e},
+	/*7 */
+	{0x000016A0, 0xC1C44d06},
+	/*8 */
+	{0x000016A0, 0xC2048514},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC807000F},
+
+	/* Both CS */
+
+	{0x00001538, 0x00000B0B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x00000F0F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_rd_667_3[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0 */
+	{0x000016A0, 0xC0020118},
+	/*1 */
+	{0x000016A0, 0xC0420108},
+	/*2 */
+	{0x000016A0, 0xC0820202},
+	/*3 */
+	{0x000016A0, 0xC0C20108},
+	/*4 */
+	{0x000016A0, 0xC1020117},
+	/*5 */
+	{0x000016A0, 0xC142010C},
+	/*6 */
+	{0x000016A0, 0xC182011B},
+	/*7 */
+	{0x000016A0, 0xC1C20107},
+	/*8 */
+	{0x000016A0, 0xC2020113},
+
+	/* Write Leveling */
+	/*0 */
+	{0x000016A0, 0xC0003600},
+	/*1 */
+	{0x000016A0, 0xC0406D0E},
+	/*2 */
+	{0x000016A0, 0xC0805207},
+	/*3 */
+	{0x000016A0, 0xC0C0A81D},
+	/*4 */
+	{0x000016A0, 0xC1009919},
+	/*5 */
+	{0x000016A0, 0xC1407911},
+	/*6 */
+	{0x000016A0, 0xC1803E02},
+	/*7 */
+	{0x000016A0, 0xC1C04D06},
+	/*8 */
+	{0x000016A0, 0xC2008113},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+
+	{0x00001538, 0x0000000B},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000F},	/*Read Data Ready Delay Register */
+
+	/*init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+MV_DRAM_TRAINING_INIT ddr3_pcac_600[MV_MAX_DDR3_STATIC_SIZE] = {
+	/* Read Leveling */
+	/*PUP   RdSampleDly (+CL)       Phase   RL ADLL value */
+	/*0 */
+	{0x000016A0, 0xC0020404},
+	/* 1           2               2               6 */
+	{0x000016A0, 0xC042031E},
+	/* 2           2               3               16 */
+	{0x000016A0, 0xC0820411},
+	/* 3           2               1               26 */
+	{0x000016A0, 0xC0C20400},
+	/* 4           2               2               29 */
+	{0x000016A0, 0xC1020404},
+	/* 5           2               2               13 */
+	{0x000016A0, 0xC142031D},
+	/* 6           2               3               6 */
+	{0x000016A0, 0xC182040C},
+	/* 7           2               1               31 */
+	{0x000016A0, 0xC1C2031B},
+	/* 8           2               2               22 */
+	{0x000016A0, 0xC2020112},
+
+	/*  Write Leveling */
+	/* 0 */
+	{0x000016A0, 0xC0004905},
+	/* 1 */
+	{0x000016A0, 0xC040A81D},
+	/* 2 */
+	{0x000016A0, 0xC0804504},
+	/* 3 */
+	{0x000016A0, 0xC0C08013},
+	/* 4 */
+	{0x000016A0, 0xC1004504},
+	/* 5 */
+	{0x000016A0, 0xC140A81D},
+	/* 6 */
+	{0x000016A0, 0xC1805909},
+	/* 7 */
+	{0x000016A0, 0xC1C09418},
+	/* 8 */
+	{0x000016A0, 0xC2006D0E},
+
+	/*center DQS on read cycle */
+	{0x000016A0, 0xC803000F},
+	{0x00001538, 0x00000009},	/*Read Data Sample Delays Register */
+	{0x0000153C, 0x0000000D},	/*Read Data Ready Delay Register */
+	/* init DRAM */
+	{0x00001480, 0x00000001},
+	{0x0, 0x0}
+};
+
+#endif /* __AXP_TRAINING_STATIC_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_axp_vars.h b/drivers/ddr/marvell/axp/ddr3_axp_vars.h
new file mode 100644
index 0000000..f00e7e6
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_axp_vars.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __AXP_VARS_H
+#define __AXP_VARS_H
+
+#include "ddr3_axp_config.h"
+#include "ddr3_axp_mc_static.h"
+#include "ddr3_axp_training_static.h"
+
+MV_DRAM_MODES ddr_modes[MV_DDR3_MODES_NUMBER] = {
+	/*      Conf name               CPUFreq         FabFreq         Chip ID Chip/Board      MC regs                 Training Values */
+	/* db board values: */
+	{"db_800-400", 0xA, 0x5, 0x0, A0, ddr3_A0_db_400, NULL},
+	{"db_1200-300", 0x2, 0xC, 0x0, A0, ddr3_A0_db_400, NULL},
+	{"db_1200-600", 0x2, 0x5, 0x0, A0, NULL, NULL},
+	{"db_1333-667", 0x3, 0x5, 0x0, A0, ddr3_A0_db_667, ddr3_db_rev2_667},
+	{"db_1600-800", 0xB, 0x5, 0x0, A0, ddr3_A0_db_667, ddr3_db_rev2_800},
+	{"amc_1333-667", 0x3, 0x5, 0x0, A0_AMC, ddr3_A0_AMC_667, NULL},
+	{"db_667-667", 0x9, 0x13, 0x0, Z1, ddr3_Z1_db_600, ddr3_db_667},
+	{"db_800-400", 0xA, 0x1, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_400},
+	{"db_1066-533", 0x1, 0x1, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_533},
+	{"db_1200-300", 0x2, 0xC, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_667},
+	{"db_1200-600", 0x2, 0x5, 0x0, Z1, ddr3_Z1_db_600, NULL},
+	{"db_1333-333", 0x3, 0xC, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_400},
+	{"db_1333-667", 0x3, 0x5, 0x0, Z1, ddr3_Z1_db_600, ddr3_db_667},
+	/* pcac board values (Z1 device): */
+	{"pcac_1200-600", 0x2, 0x5, 0x0, Z1_PCAC, ddr3_Z1_db_600,
+	 ddr3_pcac_600},
+	/* rd board values (Z1 device): */
+	{"rd_667_0", 0x3, 0x5, 0x0, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_0},
+	{"rd_667_1", 0x3, 0x5, 0x1, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_1},
+	{"rd_667_2", 0x3, 0x5, 0x2, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_2},
+	{"rd_667_3", 0x3, 0x5, 0x3, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_3}
+};
+
+/* ODT settings - if needed update the following tables: (ODT_OPT - represents the CS configuration bitmap) */
+
+u16 odt_static[ODT_OPT][MAX_CS] = {	/*        NearEnd/FarEnd */
+	{0, 0, 0, 0},		/* 0000         0/0 - Not supported */
+	{ODT40, 0, 0, 0},	/* 0001         0/1 */
+	{0, 0, 0, 0},		/* 0010         0/0 - Not supported */
+	{ODT40, ODT40, 0, 0},	/* 0011         0/2 */
+	{0, 0, ODT40, 0},	/* 0100         1/0 */
+	{ODT30, 0, ODT30, 0},	/* 0101         1/1 */
+	{0, 0, 0, 0},		/* 0110         0/0 - Not supported */
+	{ODT120, ODT20, ODT20, 0},	/* 0111         1/2 */
+	{0, 0, 0, 0},		/* 1000         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1001         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1010         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1011         0/0 - Not supported */
+	{0, 0, ODT40, 0},	/* 1100         2/0 */
+	{ODT20, 0, ODT120, ODT20},	/* 1101         2/1 */
+	{0, 0, 0, 0},		/* 1110         0/0 - Not supported */
+	{ODT120, ODT30, ODT120, ODT30}	/* 1111         2/2 */
+};
+
+u16 odt_dynamic[ODT_OPT][MAX_CS] = {	/*        NearEnd/FarEnd */
+	{0, 0, 0, 0},		/* 0000         0/0 */
+	{0, 0, 0, 0},		/* 0001         0/1 */
+	{0, 0, 0, 0},		/* 0010         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 0011         0/2 */
+	{0, 0, 0, 0},		/* 0100         1/0 */
+	{ODT120D, 0, ODT120D, 0},	/* 0101         1/1 */
+	{0, 0, 0, 0},		/* 0110         0/0 - Not supported */
+	{0, 0, ODT120D, 0},	/* 0111         1/2 */
+	{0, 0, 0, 0},		/* 1000         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1001         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1010         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1011         0/0 - Not supported */
+	{0, 0, 0, 0},		/* 1100         2/0 */
+	{ODT120D, 0, 0, 0},	/* 1101         2/1 */
+	{0, 0, 0, 0},		/* 1110         0/0 - Not supported */
+	{0, 0, 0, 0}		/* 1111         2/2 */
+};
+
+u32 odt_config[ODT_OPT] = {
+	0, 0x00010000, 0, 0x00030000, 0x04000000, 0x05050104, 0, 0x07430340, 0,
+	    0, 0, 0,
+	0x30000, 0x1C0D100C, 0, 0x3CC330C0
+};
+
+/*
+ * User can manually set SPD values (in case SPD is not available on
+ * DIMM/System).
+ * SPD Values can simplify calculating the DUNIT registers values
+ */
+u8 spd_data[SPD_SIZE] = {
+	/* AXP DB Board DIMM SPD Values - manually set */
+	0x92, 0x10, 0x0B, 0x2, 0x3, 0x19, 0x0, 0x9, 0x09, 0x52, 0x1, 0x8, 0x0C,
+	0x0, 0x7E, 0x0, 0x69, 0x78,
+	0x69, 0x30, 0x69, 0x11, 0x20, 0x89, 0x0, 0x5, 0x3C, 0x3C, 0x0, 0xF0,
+	0x82, 0x5, 0x80, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0F, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+	0x0, 0x80, 0x2C, 0x1, 0x10, 0x23, 0x35, 0x28, 0xEB, 0xCA, 0x19, 0x8F
+};
+
+/*
+ * Controller Specific configurations Starts Here - DO NOT MODIFY
+ */
+
+/* Frequency - values are 1/HCLK in ps */
+u32 cpu_fab_clk_to_hclk[FAB_OPT][CLK_CPU] =
+/* CPU Frequency:
+	1000	1066	1200	1333	1500	1666	1800	2000	600		667		800		1600	Fabric */
+{
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 3000, 2500, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 4500, 3750, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 2500, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{4000, 3750, 3333, 3000, 2666, 2400, 0, 0, 0, 0, 5000, 2500},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 3000, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{2500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 5000, 0, 4000, 0, 0, 0, 0, 0, 0, 3750},
+	{5000, 0, 0, 3750, 3333, 0, 0, 0, 0, 0, 0, 3125},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 3330, 3000, 0, 0, 0, 0, 0, 0, 0, 2500},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3750},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 3000, 2500, 0},
+	{3000, 0, 2500, 0, 0, 0, 0, 0, 0, 0, 3750, 0}
+};
+
+u32 cpu_ddr_ratios[FAB_OPT][CLK_CPU] =
+/* CPU Frequency:
+	1000	1066	1200	1333	1500	1666	1800	2000	600		667		800		1600	Fabric */
+{
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_333, DDR_400, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_444, DDR_533, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, DDR_400, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DDR_500, DDR_533, DDR_600, DDR_666, DDR_750, DDR_833, 0, 0, 0, 0,
+	 DDR_400, DDR_800},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_333, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DDR_400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, DDR_400, 0, DDR_500, 0, 0, 0, 0, 0, 0, DDR_533},
+	{DDR_400, 0, 0, DDR_533, DDR_600, 0, 0, 0, 0, 0, 0, DDR_640},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, DDR_300, DDR_333, 0, 0, 0, 0, 0, 0, 0, DDR_400},
+	{0, 0, 0, 0, 0, 0, DDR_600, DDR_666, 0, 0, 0, DDR_533},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_666, DDR_800, 0},
+	{DDR_666, 0, DDR_800, 0, 0, 0, 0, 0, 0, 0, DDR_533, 0}
+};
+
+u8 div_ratio1to1[CLK_VCO][CLK_DDR] =
+/* DDR Frequency:
+	100		300	360	400	444	500	533	600	666	750	800	833  */
+{ {0xA, 3, 0, 3, 0, 2, 0, 0, 0, 0, 0, 0},	/*  1:1     CLK_CPU_1000  */
+{0xB, 3, 0, 3, 0, 0, 2, 0, 0, 0, 0, 0},	/*  1:1     CLK_CPU_1066  */
+{0xC, 4, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0},	/*  1:1     CLK_CPU_1200  */
+{0xD, 4, 0, 4, 0, 0, 0, 0, 2, 0, 0, 0},	/*      1:1     CLK_CPU_1333  */
+{0xF, 5, 0, 4, 0, 3, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1500  */
+{0x11, 5, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1666  */
+{0x12, 6, 5, 4, 0, 0, 0, 3, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1800  */
+{0x14, 7, 0, 5, 0, 4, 0, 0, 3, 0, 0, 0},	/*      1:1     CLK_CPU_2000  */
+{0x6, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1 CLK_CPU_600   */
+{0x6, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_667   */
+{0x8, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1 CLK_CPU_800   */
+{0x10, 5, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1 CLK_CPU_1600   */
+{0x14, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0},	/*  1:1     CLK_CPU_1000 VCO_2000 */
+{0x15, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0},	/*  1:1     CLK_CPU_1066 VCO_2133 */
+{0x18, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0},	/*  1:1     CLK_CPU_1200 VCO_2400 */
+{0x1A, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1333 VCO_2666 */
+{0x1E, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1500 VCO_3000 */
+{0x21, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1666 VCO_3333 */
+{0x24, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_1800 VCO_3600 */
+{0x28, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_2000 VCO_4000 */
+{0xC, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1 CLK_CPU_600 VCO_1200 */
+{0xD, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},	/*      1:1     CLK_CPU_667 VCO_1333 */
+{0x10, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},	/*  1:1 CLK_CPU_800 VCO_1600 */
+{0x20, 10, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}	/*      1:1 CLK_CPU_1600 VCO_3200 */
+};
+
+u8 div_ratio2to1[CLK_VCO][CLK_DDR] =
+/* DDR Frequency:
+		100	300	360	400	444	500	533	600	666	750	800	833  */
+{ {0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0},	/*      2:1     CLK_CPU_1000  */
+{0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_1066  */
+{0, 0, 0, 3, 5, 0, 0, 2, 0, 0, 3, 3},	/*      2:1     CLK_CPU_1200  */
+{0, 0, 0, 0, 0, 0, 5, 0, 2, 0, 3, 0},	/*      2:1     CLK_CPU_1333  */
+{0, 0, 0, 0, 0, 3, 0, 5, 0, 2, 0, 0},	/*      2:1     CLK_CPU_1500  */
+{0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 2},	/*      2:1     CLK_CPU_1666  */
+{0, 0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 0},	/*      2:1     CLK_CPU_1800  */
+{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 5},	/*      2:1     CLK_CPU_2000  */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_600   */
+{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},	/*  2:1 CLK_CPU_667   */
+{0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0},	/*      2:1 CLK_CPU_800   */
+{0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0},	/*      2:1 CLK_CPU_1600   */
+{0, 0, 0, 5, 0, 0, 0, 0, 3, 0, 0, 0},	/*      2:1     CLK_CPU_1000 VCO_2000 */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_1066 VCO_2133 */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0},	/*      2:1     CLK_CPU_1200 VCO_2400 */
+{0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_1333 VCO_2666 */
+{0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0},	/*      2:1     CLK_CPU_1500 VCO_3000 */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_1666 VCO_3333 */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_1800 VCO_3600 */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_2000 VCO_4000 */
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/*      2:1     CLK_CPU_600 VCO_1200 */
+{0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0},	/*  2:1 CLK_CPU_667 VCO_1333 */
+{0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0},	/*      2:1 CLK_CPU_800 VCO_1600 */
+{0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0}	/*      2:1 CLK_CPU_1600 VCO_3200 */
+};
+
+#endif /* __AXP_VARS_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_dfs.c b/drivers/ddr/marvell/axp/ddr3_dfs.c
new file mode 100644
index 0000000..ba89959
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_dfs.c
@@ -0,0 +1,1551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_hw_training.h"
+
+/*
+ * Debug
+ */
+#define DEBUG_DFS_C(s, d, l) \
+	DEBUG_DFS_S(s); DEBUG_DFS_D(d, l); DEBUG_DFS_S("\n")
+#define DEBUG_DFS_FULL_C(s, d, l) \
+	DEBUG_DFS_FULL_S(s); DEBUG_DFS_FULL_D(d, l); DEBUG_DFS_FULL_S("\n")
+
+#ifdef MV_DEBUG_DFS
+#define DEBUG_DFS_S(s)			puts(s)
+#define DEBUG_DFS_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_DFS_S(s)
+#define DEBUG_DFS_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_DFS_FULL
+#define DEBUG_DFS_FULL_S(s)		puts(s)
+#define DEBUG_DFS_FULL_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_DFS_FULL_S(s)
+#define DEBUG_DFS_FULL_D(d, l)
+#endif
+
+#if defined(MV88F672X)
+extern u8 div_ratio[CLK_VCO][CLK_DDR];
+extern void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps);
+#else
+extern u16 odt_dynamic[ODT_OPT][MAX_CS];
+extern u8 div_ratio1to1[CLK_CPU][CLK_DDR];
+extern u8 div_ratio2to1[CLK_CPU][CLK_DDR];
+#endif
+extern u16 odt_static[ODT_OPT][MAX_CS];
+
+extern u32 cpu_fab_clk_to_hclk[FAB_OPT][CLK_CPU];
+
+extern u32 ddr3_get_vco_freq(void);
+
+u32 ddr3_get_freq_parameter(u32 target_freq, int ratio_2to1);
+
+#ifdef MV_DEBUG_DFS
+static inline void dfs_reg_write(u32 addr, u32 val)
+{
+	printf("\n write reg 0x%08x = 0x%08x", addr, val);
+	writel(val, INTER_REGS_BASE + addr);
+}
+#else
+static inline void dfs_reg_write(u32 addr, u32 val)
+{
+	writel(val, INTER_REGS_BASE + addr);
+}
+#endif
+
+static void wait_refresh_op_complete(void)
+{
+	u32 reg;
+
+	/* Poll - Wait for Refresh operation completion */
+	do {
+		reg = reg_read(REG_SDRAM_OPERATION_ADDR) &
+			REG_SDRAM_OPERATION_CMD_RFRS_DONE;
+	} while (reg);		/* Wait for '0' */
+}
+
+/*
+ * Name:     ddr3_get_freq_parameter
+ * Desc:     Finds CPU/DDR frequency ratio according to Sample@reset and table.
+ * Args:     target_freq - target frequency
+ * Notes:
+ * Returns:  freq_par - the ratio parameter
+ */
+u32 ddr3_get_freq_parameter(u32 target_freq, int ratio_2to1)
+{
+	u32 ui_vco_freq, freq_par;
+
+	ui_vco_freq = ddr3_get_vco_freq();
+
+#if defined(MV88F672X)
+	freq_par = div_ratio[ui_vco_freq][target_freq];
+#else
+	/* Find the ratio between PLL frequency and ddr-clk */
+	if (ratio_2to1)
+		freq_par = div_ratio2to1[ui_vco_freq][target_freq];
+	else
+		freq_par = div_ratio1to1[ui_vco_freq][target_freq];
+#endif
+
+	return freq_par;
+}
+
+/*
+ * Name:     ddr3_dfs_high_2_low
+ * Desc:
+ * Args:     freq - target frequency
+ * Notes:
+ * Returns:  MV_OK - success, MV_FAIL - fail
+ */
+int ddr3_dfs_high_2_low(u32 freq, MV_DRAM_INFO *dram_info)
+{
+#if defined(MV88F78X60) || defined(MV88F672X)
+	/* This Flow is relevant for ArmadaXP A0 */
+	u32 reg, freq_par, tmp;
+	u32 cs = 0;
+
+	DEBUG_DFS_C("DDR3 - DFS - High To Low - Starting DFS procedure to Frequency - ",
+		    freq, 1);
+
+	/* target frequency - 100MHz */
+	freq_par = ddr3_get_freq_parameter(freq, 0);
+
+#if defined(MV88F672X)
+	u32 hclk;
+	u32 cpu_freq = ddr3_get_cpu_freq();
+	get_target_freq(cpu_freq, &tmp, &hclk);
+#endif
+
+	/* Configure - DRAM DLL final state after DFS is complete - Enable */
+	reg = reg_read(REG_DFS_ADDR);
+	/* [0] - DfsDllNextState - Disable */
+	reg |= (1 << REG_DFS_DLLNEXTSTATE_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Configure - XBAR Retry response during Block to enable internal
+	 * access - Disable
+	 */
+	reg = reg_read(REG_METAL_MASK_ADDR);
+	/* [0] - RetryMask - Disable */
+	reg &= ~(1 << REG_METAL_MASK_RETRY_OFFS);
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	/* Configure - Block new external transactions - Enable */
+	reg = reg_read(REG_DFS_ADDR);
+	reg |= (1 << REG_DFS_BLOCK_OFFS);	/* [1] - DfsBlock - Enable  */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Registered DIMM support */
+	if (dram_info->reg_dimm) {
+		/*
+		 * Configure - Disable Register DIMM CKE Power
+		 * Down mode - CWA_RC
+		 */
+		reg = (0x9 & REG_SDRAM_OPERATION_CWA_RC_MASK) <<
+			REG_SDRAM_OPERATION_CWA_RC_OFFS;
+		/*
+		 * Configure - Disable Register DIMM CKE Power
+		 * Down mode - CWA_DATA
+		 */
+		reg |= ((0 & REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+			REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+
+		/*
+		 * Configure - Disable Register DIMM CKE Power
+		 * Down mode - Set Delay - tMRD
+		 */
+		reg |= (0 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS);
+
+		/* Configure - Issue CWA command with the above parameters */
+		reg |= (REG_SDRAM_OPERATION_CMD_CWA &
+			~(0xF << REG_SDRAM_OPERATION_CS_OFFS));
+
+		/* 0x1418 - SDRAM Operation Register */
+		dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+		/* Poll - Wait for CWA operation completion */
+		do {
+			reg = reg_read(REG_SDRAM_OPERATION_ADDR) &
+			       (REG_SDRAM_OPERATION_CMD_MASK);
+		} while (reg);
+
+		/* Configure - Disable outputs floating during Self Refresh */
+		reg = reg_read(REG_REGISTERED_DRAM_CTRL_ADDR);
+		/* [15] - SRFloatEn - Disable */
+		reg &= ~(1 << REG_REGISTERED_DRAM_CTRL_SR_FLOAT_OFFS);
+		/* 0x16D0 - DDR3 Registered DRAM Control */
+		dfs_reg_write(REG_REGISTERED_DRAM_CTRL_ADDR, reg);
+	}
+
+	/* Optional - Configure - DDR3_Rtt_nom_CS# */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = reg_read(REG_DDR3_MR1_CS_ADDR +
+				       (cs << MR_CS_ADDR_OFFS));
+			reg &= REG_DDR3_MR1_RTT_MASK;
+			dfs_reg_write(REG_DDR3_MR1_CS_ADDR +
+				      (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	/* Configure - Move DRAM into Self Refresh */
+	reg = reg_read(REG_DFS_ADDR);
+	reg |= (1 << REG_DFS_SR_OFFS);	/* [2] - DfsSR - Enable */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Poll - Wait for Self Refresh indication */
+	do {
+		reg = ((reg_read(REG_DFS_ADDR)) & (1 << REG_DFS_ATSR_OFFS));
+	} while (reg == 0x0);	/* 0x1528 [3] - DfsAtSR - Wait for '1' */
+
+	/* Start of clock change procedure (PLL) */
+#if defined(MV88F672X)
+	/* avantaLP */
+	/* Configure    cpupll_clkdiv_reset_mask */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0);
+	reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL0_MASK;
+	/* 0xE8264[7:0]   0xff CPU Clock Dividers Reset mask */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, (reg + 0xFF));
+
+	/* Configure    cpu_clkdiv_reload_smooth    */
+	reg = reg_read(CPU_PLL_CNTRL0);
+	reg &= CPU_PLL_CNTRL0_RELOAD_SMOOTH_MASK;
+	/* 0xE8260   [15:8]  0x2 CPU Clock Dividers Reload Smooth enable */
+	dfs_reg_write(CPU_PLL_CNTRL0,
+		      (reg + (2 << CPU_PLL_CNTRL0_RELOAD_SMOOTH_OFFS)));
+
+	/* Configure    cpupll_clkdiv_relax_en */
+	reg = reg_read(CPU_PLL_CNTRL0);
+	reg &= CPU_PLL_CNTRL0_RELAX_EN_MASK;
+	/* 0xE8260 [31:24] 0x2 Relax Enable */
+	dfs_reg_write(CPU_PLL_CNTRL0,
+		      (reg + (2 << CPU_PLL_CNTRL0_RELAX_EN_OFFS)));
+
+	/* Configure    cpupll_clkdiv_ddr_clk_ratio */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL1);
+	/*
+	 * 0xE8268  [13:8]  N   Set Training clock:
+	 * APLL Out Clock (VCO freq) / N = 100 MHz
+	 */
+	reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL1_MASK;
+	reg |= (freq_par << 8);	/* full Integer ratio from PLL-out to ddr-clk */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL1, reg);
+
+	/* Configure    cpupll_clkdiv_reload_ratio  */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0);
+	reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK;
+	/* 0xE8264 [8]=0x1 CPU Clock Dividers Reload Ratio trigger set */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0,
+		      (reg + (1 << CPU_PLL_CLOCK_RELOAD_RATIO_OFFS)));
+
+	udelay(1);
+
+	/* Configure    cpupll_clkdiv_reload_ratio  */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0);
+	reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK;
+	/* 0xE8264 [8]=0x0 CPU Clock Dividers Reload Ratio trigger clear */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, reg);
+
+	udelay(5);
+
+#else
+	/*
+	 * Initial Setup - assure that the "load new ratio" is clear (bit 24)
+	 * and in the same chance, block reassertions of reset [15:8] and
+	 * force reserved bits[7:0].
+	 */
+	reg = 0x0000FDFF;
+	/* 0x18700 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	/*
+	 * RelaX whenever reset is asserted to that channel
+	 * (good for any case)
+	 */
+	reg = 0x0000FF00;
+	/* 0x18704 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg);
+
+	reg = reg_read(REG_CPU_DIV_CLK_CTRL_2_ADDR) &
+		REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK;
+
+	/* full Integer ratio from PLL-out to ddr-clk */
+	reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS);
+	/* 0x1870C - CPU Div CLK control 3 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_2_ADDR, reg);
+
+	/*
+	 * Shut off clock enable to the DDRPHY clock channel (this is the "D").
+	 * All the rest are kept as is (forced, but could be read-modify-write).
+	 * This is done now by RMW above.
+	 */
+
+	/* Clock is not shut off gracefully - keep it running */
+	reg = 0x000FFF02;
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg);
+
+	/* Wait before replacing the clock on the DDR Phy Channel. */
+	udelay(1);
+
+	/*
+	 * This for triggering the frequency update. Bit[24] is the
+	 * central control
+	 * bits [23:16] == which channels to change ==2 ==>
+	 *                 only DDR Phy (smooth transition)
+	 * bits [15:8] == mask reset reassertion due to clock modification
+	 *                to these channels.
+	 * bits [7:0] == not in use
+	 */
+	reg = 0x0102FDFF;
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(1);		/* Wait 1usec */
+
+	/*
+	 * Poll Div CLK status 0 register - indication that the clocks
+	 * are active - 0x18718 [8]
+	 */
+	do {
+		reg = (reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR)) &
+			(1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS);
+	} while (reg == 0);
+
+	/*
+	 * Clean the CTRL0, to be ready for next resets and next requests
+	 * of ratio modifications.
+	 */
+	reg = 0x000000FF;
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(5);
+#endif
+	/* End of clock change procedure (PLL) */
+
+	/* Configure - Select normal clock for the DDR PHY - Enable */
+	reg = reg_read(REG_DRAM_INIT_CTRL_STATUS_ADDR);
+	/* [16] - ddr_phy_trn_clk_sel - Enable  */
+	reg |= (1 << REG_DRAM_INIT_CTRL_TRN_CLK_OFFS);
+	/* 0x18488 - DRAM Init control status register */
+	dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg);
+
+	/* Configure - Set Correct Ratio - 1:1 */
+	/* [15] - Phy2UnitClkRatio = 0 - Set 1:1 Ratio between Dunit and Phy */
+
+	reg = reg_read(REG_DDR_IO_ADDR) & ~(1 << REG_DDR_IO_CLK_RATIO_OFFS);
+	dfs_reg_write(REG_DDR_IO_ADDR, reg);	/* 0x1524 - DDR IO Register */
+
+	/* Configure - 2T Mode - Restore original configuration */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	/* [3:4] 2T - 1T Mode - low freq */
+	reg &= ~(REG_DUNIT_CTRL_LOW_2T_MASK << REG_DUNIT_CTRL_LOW_2T_OFFS);
+	/* 0x1404 - DDR Controller Control Low Register */
+	dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+
+	/* Configure - Restore CL and CWL - MRS Commands */
+	reg = reg_read(REG_DFS_ADDR);
+	reg &= ~(REG_DFS_CL_NEXT_STATE_MASK << REG_DFS_CL_NEXT_STATE_OFFS);
+	reg &= ~(REG_DFS_CWL_NEXT_STATE_MASK << REG_DFS_CWL_NEXT_STATE_OFFS);
+	/* [8] - DfsCLNextState - MRS CL=6 after DFS (due to DLL-off mode) */
+	reg |= (0x4 << REG_DFS_CL_NEXT_STATE_OFFS);
+	/* [12] - DfsCWLNextState - MRS CWL=6 after DFS (due to DLL-off mode) */
+	reg |= (0x1 << REG_DFS_CWL_NEXT_STATE_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Poll - Wait for APLL + ADLLs lock on new frequency */
+	do {
+		reg = (reg_read(REG_PHY_LOCK_STATUS_ADDR)) &
+			REG_PHY_LOCK_APLL_ADLL_STATUS_MASK;
+		/* 0x1674 [10:0] - Phy lock status Register */
+	} while (reg != REG_PHY_LOCK_APLL_ADLL_STATUS_MASK);
+
+	/* Configure - Reset the PHY Read FIFO and Write channels - Set Reset */
+	reg = (reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK);
+	/* [30:29] = 0 - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/*
+	 * Configure - DRAM Data PHY Read [30], Write [29] path
+	 * reset - Release Reset
+	 */
+	reg = (reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK);
+	/* [30:29] = '11' - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/* Registered DIMM support */
+	if (dram_info->reg_dimm) {
+		/*
+		 * Configure - Change register DRAM operating speed
+		 * (below 400MHz) - CWA_RC
+		 */
+		reg = (0xA & REG_SDRAM_OPERATION_CWA_RC_MASK) <<
+			REG_SDRAM_OPERATION_CWA_RC_OFFS;
+
+		/*
+		 * Configure - Change register DRAM operating speed
+		 * (below 400MHz) - CWA_DATA
+		 */
+		reg |= ((0x0 & REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+			REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+
+		/* Configure - Set Delay - tSTAB */
+		reg |= (0x1 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS);
+
+		/* Configure - Issue CWA command with the above parameters */
+		reg |= (REG_SDRAM_OPERATION_CMD_CWA &
+			~(0xF << REG_SDRAM_OPERATION_CS_OFFS));
+
+		/* 0x1418 - SDRAM Operation Register */
+		dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+		/* Poll - Wait for CWA operation completion */
+		do {
+			reg = reg_read(REG_SDRAM_OPERATION_ADDR) &
+				(REG_SDRAM_OPERATION_CMD_MASK);
+		} while (reg);
+	}
+
+	/* Configure - Exit Self Refresh */
+	/* [2] - DfsSR  */
+	reg = (reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS));
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Poll - DFS Register - 0x1528 [3] - DfsAtSR - All DRAM devices
+	 * on all ranks are NOT in self refresh mode
+	 */
+	do {
+		reg = ((reg_read(REG_DFS_ADDR)) & (1 << REG_DFS_ATSR_OFFS));
+	} while (reg);		/* Wait for '0' */
+
+	/* Configure - Issue Refresh command */
+	/* [3-0] = 0x2 - Refresh Command, [11-8] - enabled Cs */
+	reg = REG_SDRAM_OPERATION_CMD_RFRS;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs))
+			reg &= ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+	}
+
+	/* 0x1418 - SDRAM Operation Register */
+	dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+	/* Poll - Wait for Refresh operation completion */
+	wait_refresh_op_complete();
+
+	/* Configure - Block new external transactions - Disable */
+	reg = reg_read(REG_DFS_ADDR);
+	reg &= ~(1 << REG_DFS_BLOCK_OFFS);	/* [1] - DfsBlock - Disable  */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Configure -  XBAR Retry response during Block to enable
+	 * internal access - Disable
+	 */
+	reg = reg_read(REG_METAL_MASK_ADDR);
+	/* [0] - RetryMask - Enable */
+	reg |= (1 << REG_METAL_MASK_RETRY_OFFS);
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			/* Configure - Set CL */
+			reg = reg_read(REG_DDR3_MR0_CS_ADDR +
+				       (cs << MR_CS_ADDR_OFFS)) &
+				~REG_DDR3_MR0_CL_MASK;
+			tmp = 0x4;	/* CL=6 - 0x4 */
+			reg |= ((tmp & 0x1) << REG_DDR3_MR0_CL_OFFS);
+			reg |= ((tmp & 0xE) << REG_DDR3_MR0_CL_HIGH_OFFS);
+			dfs_reg_write(REG_DDR3_MR0_CS_ADDR +
+				      (cs << MR_CS_ADDR_OFFS), reg);
+
+			/* Configure - Set CWL */
+			reg = reg_read(REG_DDR3_MR2_CS_ADDR +
+				       (cs << MR_CS_ADDR_OFFS))
+				& ~(REG_DDR3_MR2_CWL_MASK << REG_DDR3_MR2_CWL_OFFS);
+			/* CWL=6 - 0x1 */
+			reg |= ((0x1) << REG_DDR3_MR2_CWL_OFFS);
+			dfs_reg_write(REG_DDR3_MR2_CS_ADDR +
+				      (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	DEBUG_DFS_C("DDR3 - DFS - High To Low - Ended successfuly - new Frequency - ",
+		    freq, 1);
+
+	return MV_OK;
+#else
+	/* This Flow is relevant for Armada370 A0 and ArmadaXP Z1 */
+
+	u32 reg, freq_par;
+	u32 cs = 0;
+
+	DEBUG_DFS_C("DDR3 - DFS - High To Low - Starting DFS procedure to Frequency - ",
+		    freq, 1);
+
+	/* target frequency - 100MHz */
+	freq_par = ddr3_get_freq_parameter(freq, 0);
+
+	reg = 0x0000FF00;
+	/* 0x18700 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg);
+
+	/* 0x1600 - ODPG_CNTRL_Control */
+	reg = reg_read(REG_ODPG_CNTRL_ADDR);
+	/* [21] = 1 - auto refresh disable */
+	reg |= (1 << REG_ODPG_CNTRL_OFFS);
+	dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg);
+
+	/* 0x1670 - PHY lock mask register */
+	reg = reg_read(REG_PHY_LOCK_MASK_ADDR);
+	reg &= REG_PHY_LOCK_MASK_MASK;	/* [11:0] = 0 */
+	dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg);
+
+	reg = reg_read(REG_DFS_ADDR);	/* 0x1528 - DFS register */
+
+	/* Disable reconfig */
+	reg &= ~0x10;	/* [4] - Enable reconfig MR registers after DFS_ERG */
+	reg |= 0x1;	/* [0] - DRAM DLL disabled after DFS */
+
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	reg = reg_read(REG_METAL_MASK_ADDR) & ~(1 << 0); /* [0] - disable */
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	/* [1] - DFS Block enable  */
+	reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_BLOCK_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* [2] - DFS Self refresh enable  */
+	reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_SR_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Poll DFS Register - 0x1528 [3] - DfsAtSR -
+	 * All DRAM devices on all ranks are in self refresh mode -
+	 * DFS can be executed afterwards
+	 */
+	do {
+		reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS);
+	} while (reg == 0x0);	/* Wait for '1' */
+
+	/* Disable ODT on DLL-off mode */
+	dfs_reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR,
+		      REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK);
+
+	/* [11:0] = 0 */
+	reg = (reg_read(REG_PHY_LOCK_MASK_ADDR) & REG_PHY_LOCK_MASK_MASK);
+	/* 0x1670 - PHY lock mask register */
+	dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg);
+
+	/* Add delay between entering SR and start ratio modification */
+	udelay(1);
+
+	/*
+	 * Initial Setup - assure that the "load new ratio" is clear (bit 24)
+	 * and in the same chance, block reassertions of reset [15:8] and
+	 * force reserved bits[7:0].
+	 */
+	reg = 0x0000FDFF;
+	/* 0x18700 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	/*
+	 * RelaX whenever reset is asserted to that channel (good for any case)
+	 */
+	reg = 0x0000FF00;
+	/* 0x18700 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg);
+
+	reg = reg_read(REG_CPU_DIV_CLK_CTRL_3_ADDR) &
+		REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK;
+	/* Full Integer ratio from PLL-out to ddr-clk */
+	reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS);
+	/* 0x1870C - CPU Div CLK control 3 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_3_ADDR, reg);
+
+	/*
+	 * Shut off clock enable to the DDRPHY clock channel (this is the "D").
+	 * All the rest are kept as is (forced, but could be read-modify-write).
+	 * This is done now by RMW above.
+	 */
+
+	/* Clock is not shut off gracefully - keep it running */
+	reg = 0x000FFF02;
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg);
+
+	/* Wait before replacing the clock on the DDR Phy Channel. */
+	udelay(1);
+
+	/*
+	 * This for triggering the frequency update. Bit[24] is the
+	 * central control
+	 * bits [23:16] == which channels to change ==2 ==> only DDR Phy
+	 *                 (smooth transition)
+	 * bits [15:8] == mask reset reassertion due to clock modification
+	 *                to these channels.
+	 * bits [7:0] == not in use
+	 */
+	reg = 0x0102FDFF;
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(1);		/* Wait 1usec */
+
+	/*
+	 * Poll Div CLK status 0 register - indication that the clocks
+	 * are active - 0x18718 [8]
+	 */
+	do {
+		reg = (reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR)) &
+			(1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS);
+	} while (reg == 0);
+
+	/*
+	 * Clean the CTRL0, to be ready for next resets and next requests of
+	 * ratio modifications.
+	 */
+	reg = 0x000000FF;
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(5);
+
+	/* Switch HCLK Mux to training clk (100Mhz), keep DFS request bit */
+	reg = 0x20050000;
+	/* 0x18488 - DRAM Init control status register */
+	dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg);
+
+	reg = reg_read(REG_DDR_IO_ADDR) & ~(1 << REG_DDR_IO_CLK_RATIO_OFFS);
+	/* [15] = 0 - Set 1:1 Ratio between Dunit and Phy */
+	dfs_reg_write(REG_DDR_IO_ADDR, reg);	/* 0x1524 - DDR IO Regist */
+
+	reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) & REG_DRAM_PHY_CONFIG_MASK;
+	/* [31:30]] - reset pup data ctrl ADLL */
+	/* 0x15EC - DRAM PHY Config register */
+	dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+	reg = (reg_read(REG_DRAM_PHY_CONFIG_ADDR) | ~REG_DRAM_PHY_CONFIG_MASK);
+	/* [31:30] - normal pup data ctrl ADLL */
+	/* 0x15EC - DRAM PHY Config register */
+	dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+	udelay(1);		/* Wait 1usec */
+
+	/* 0x1404 */
+	reg = (reg_read(REG_DUNIT_CTRL_LOW_ADDR) & 0xFFFFFFE7);
+	dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+
+	/* Poll Phy lock status register - APLL lock indication - 0x1674 */
+	do {
+		reg = (reg_read(REG_PHY_LOCK_STATUS_ADDR)) &
+			REG_PHY_LOCK_STATUS_LOCK_MASK;
+	} while (reg != REG_PHY_LOCK_STATUS_LOCK_MASK);	/* Wait for '0xFFF' */
+
+	reg = (reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK);
+	/* [30:29] = 0 - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK;
+	/* [30:29] = '11' - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	udelay(1000);		/* Wait 1msec */
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			/* Config CL and CWL with MR0 and MR2 registers */
+			reg = reg_read(REG_DDR3_MR0_ADDR);
+			reg &= ~0x74;	/* CL [3:0]; [6:4],[2] */
+			reg |= (1 << 5);	/* CL = 4, CAS is 6 */
+			dfs_reg_write(REG_DDR3_MR0_ADDR, reg);
+			reg = REG_SDRAM_OPERATION_CMD_MR0 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/* 0x1418 - SDRAM Operation Register */
+			dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			reg = reg_read(REG_DDR3_MR2_ADDR);
+			reg &= ~0x38;	/* CWL [5:3] */
+			reg |= (1 << 3);	/* CWL = 1, CWL is 6 */
+			dfs_reg_write(REG_DDR3_MR2_ADDR, reg);
+
+			reg = REG_SDRAM_OPERATION_CMD_MR2 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/* 0x1418 - SDRAM Operation Register */
+			dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			/* Set current rd_sample_delay  */
+			reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+			reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK <<
+				 (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			reg |= (5 << (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			dfs_reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg);
+
+			/* Set current rd_ready_delay  */
+			reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+			reg &= ~(REG_READ_DATA_READY_DELAYS_MASK <<
+				 (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+			reg |= ((6) << (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+			dfs_reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+		}
+	}
+
+	/* [2] - DFS Self refresh disable  */
+	reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* [1] - DFS Block enable  */
+	reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_BLOCK_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Poll DFS Register - 0x1528 [3] - DfsAtSR -
+	 * All DRAM devices on all ranks are in self refresh mode - DFS can
+	 * be executed afterwards
+	 */
+	do {
+		reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS);
+	} while (reg);		/* Wait for '1' */
+
+	reg = (reg_read(REG_METAL_MASK_ADDR) | (1 << 0));
+	/* [0] - Enable Dunit to crossbar retry */
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	/* 0x1600 - PHY lock mask register */
+	reg = reg_read(REG_ODPG_CNTRL_ADDR);
+	reg &= ~(1 << REG_ODPG_CNTRL_OFFS);	/* [21] = 0 */
+	dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg);
+
+	/* 0x1670 - PHY lock mask register */
+	reg = reg_read(REG_PHY_LOCK_MASK_ADDR);
+	reg |= ~REG_PHY_LOCK_MASK_MASK;	/* [11:0] = FFF */
+	dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg);
+
+	DEBUG_DFS_C("DDR3 - DFS - High To Low - Ended successfuly - new Frequency - ",
+		    freq, 1);
+
+	return MV_OK;
+#endif
+}
+
+/*
+ * Name:     ddr3_dfs_low_2_high
+ * Desc:
+ * Args:     freq - target frequency
+ * Notes:
+ * Returns:  MV_OK - success, MV_FAIL - fail
+ */
+int ddr3_dfs_low_2_high(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info)
+{
+#if defined(MV88F78X60) || defined(MV88F672X)
+	/* This Flow is relevant for ArmadaXP A0 */
+	u32 reg, freq_par, tmp;
+	u32 cs = 0;
+
+	DEBUG_DFS_C("DDR3 - DFS - Low To High - Starting DFS procedure to Frequency - ",
+		    freq, 1);
+
+	/* target frequency - freq */
+	freq_par = ddr3_get_freq_parameter(freq, ratio_2to1);
+
+#if defined(MV88F672X)
+	u32 hclk;
+	u32 cpu_freq = ddr3_get_cpu_freq();
+	get_target_freq(cpu_freq, &tmp, &hclk);
+#endif
+
+	/* Configure - DRAM DLL final state after DFS is complete - Enable */
+	reg = reg_read(REG_DFS_ADDR);
+	/* [0] - DfsDllNextState - Enable */
+	reg &= ~(1 << REG_DFS_DLLNEXTSTATE_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Configure -  XBAR Retry response during Block to enable
+	 * internal access - Disable
+	 */
+	reg = reg_read(REG_METAL_MASK_ADDR);
+	/* [0] - RetryMask - Disable */
+	reg &= ~(1 << REG_METAL_MASK_RETRY_OFFS);
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	/* Configure - Block new external transactions - Enable */
+	reg = reg_read(REG_DFS_ADDR);
+	reg |= (1 << REG_DFS_BLOCK_OFFS);	/* [1] - DfsBlock - Enable  */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Configure - Move DRAM into Self Refresh */
+	reg = reg_read(REG_DFS_ADDR);
+	reg |= (1 << REG_DFS_SR_OFFS);	/* [2] - DfsSR - Enable */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Poll - Wait for Self Refresh indication */
+	do {
+		reg = ((reg_read(REG_DFS_ADDR)) & (1 << REG_DFS_ATSR_OFFS));
+	} while (reg == 0x0);	/* 0x1528 [3] - DfsAtSR - Wait for '1' */
+
+	/* Start of clock change procedure (PLL) */
+#if defined(MV88F672X)
+	/* avantaLP */
+	/* Configure    cpupll_clkdiv_reset_mask */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0);
+	reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL0_MASK;
+	/* 0xE8264[7:0]   0xff CPU Clock Dividers Reset mask */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, (reg + 0xFF));
+
+	/* Configure    cpu_clkdiv_reload_smooth    */
+	reg = reg_read(CPU_PLL_CNTRL0);
+	reg &= CPU_PLL_CNTRL0_RELOAD_SMOOTH_MASK;
+	/* 0xE8260   [15:8]  0x2 CPU Clock Dividers Reload Smooth enable */
+	dfs_reg_write(CPU_PLL_CNTRL0,
+		      reg + (2 << CPU_PLL_CNTRL0_RELOAD_SMOOTH_OFFS));
+
+	/* Configure    cpupll_clkdiv_relax_en */
+	reg = reg_read(CPU_PLL_CNTRL0);
+	reg &= CPU_PLL_CNTRL0_RELAX_EN_MASK;
+	/* 0xE8260 [31:24] 0x2 Relax Enable */
+	dfs_reg_write(CPU_PLL_CNTRL0,
+		      reg + (2 << CPU_PLL_CNTRL0_RELAX_EN_OFFS));
+
+	/* Configure    cpupll_clkdiv_ddr_clk_ratio */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL1);
+	/*
+	 * 0xE8268  [13:8]  N   Set Training clock:
+	 * APLL Out Clock (VCO freq) / N = 100 MHz
+	 */
+	reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL1_MASK;
+	reg |= (freq_par << 8);	/* full Integer ratio from PLL-out to ddr-clk */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL1, reg);
+	/* Configure    cpupll_clkdiv_reload_ratio  */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0);
+	reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK;
+	/* 0xE8264 [8]=0x1 CPU Clock Dividers Reload Ratio trigger set */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0,
+		      reg + (1 << CPU_PLL_CLOCK_RELOAD_RATIO_OFFS));
+
+	udelay(1);
+
+	/* Configure    cpupll_clkdiv_reload_ratio  */
+	reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0);
+	reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK;
+	/* 0xE8264 [8]=0x0 CPU Clock Dividers Reload Ratio trigger clear */
+	dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, reg);
+
+	udelay(5);
+
+#else
+	/*
+	 * Initial Setup - assure that the "load new ratio" is clear (bit 24)
+	 * and in the same chance, block reassertions of reset [15:8]
+	 * and force reserved bits[7:0].
+	 */
+	reg = 0x0000FFFF;
+
+	/* 0x18700 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	/*
+	 * RelaX whenever reset is asserted to that channel (good for any case)
+	 */
+	reg = 0x0000FF00;
+	/* 0x18704 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg);
+
+	reg = reg_read(REG_CPU_DIV_CLK_CTRL_2_ADDR) &
+		REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK;
+	reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS);
+	/* full Integer ratio from PLL-out to ddr-clk */
+	/* 0x1870C - CPU Div CLK control 3 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_2_ADDR, reg);
+
+	/*
+	 * Shut off clock enable to the DDRPHY clock channel (this is the "D").
+	 * All the rest are kept as is (forced, but could be read-modify-write).
+	 * This is done now by RMW above.
+	 */
+	reg = 0x000FFF02;
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg);
+
+	/* Wait before replacing the clock on the DDR Phy Channel. */
+	udelay(1);
+
+	reg = 0x0102FDFF;
+	/*
+	 * This for triggering the frequency update. Bit[24] is the
+	 * central control
+	 * bits [23:16] == which channels to change ==2 ==> only DDR Phy
+	 *                 (smooth transition)
+	 * bits [15:8] == mask reset reassertion due to clock modification
+	 *                to these channels.
+	 * bits [7:0] == not in use
+	 */
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(1);
+
+	/*
+	 * Poll Div CLK status 0 register - indication that the clocks
+	 * are active - 0x18718 [8]
+	 */
+	do {
+		reg = reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR) &
+			(1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS);
+	} while (reg == 0);
+
+	reg = 0x000000FF;
+	/*
+	 * Clean the CTRL0, to be ready for next resets and next requests
+	 * of ratio modifications.
+	 */
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+#endif
+	/* End of clock change procedure (PLL) */
+
+	if (ratio_2to1) {
+		/* Configure - Select normal clock for the DDR PHY - Disable */
+		reg = reg_read(REG_DRAM_INIT_CTRL_STATUS_ADDR);
+		/* [16] - ddr_phy_trn_clk_sel - Disable  */
+		reg &= ~(1 << REG_DRAM_INIT_CTRL_TRN_CLK_OFFS);
+		/* 0x18488 - DRAM Init control status register */
+		dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg);
+	}
+
+	/*
+	 * Configure - Set Correct Ratio - according to target ratio
+	 * parameter - 2:1/1:1
+	 */
+	if (ratio_2to1) {
+		/*
+		 * [15] - Phy2UnitClkRatio = 1 - Set 2:1 Ratio between
+		 * Dunit and Phy
+		 */
+		reg = reg_read(REG_DDR_IO_ADDR) |
+			(1 << REG_DDR_IO_CLK_RATIO_OFFS);
+	} else {
+		/*
+		 * [15] - Phy2UnitClkRatio = 0 - Set 1:1 Ratio between
+		 * Dunit and Phy
+		 */
+		reg = reg_read(REG_DDR_IO_ADDR) &
+			~(1 << REG_DDR_IO_CLK_RATIO_OFFS);
+	}
+	dfs_reg_write(REG_DDR_IO_ADDR, reg);	/* 0x1524 - DDR IO Register */
+
+	/* Configure - 2T Mode - Restore original configuration */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	/* [3:4] 2T - Restore value */
+	reg &= ~(REG_DUNIT_CTRL_LOW_2T_MASK << REG_DUNIT_CTRL_LOW_2T_OFFS);
+	reg |= ((dram_info->mode_2t & REG_DUNIT_CTRL_LOW_2T_MASK) <<
+		REG_DUNIT_CTRL_LOW_2T_OFFS);
+	/* 0x1404 - DDR Controller Control Low Register */
+	dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+
+	/* Configure - Restore CL and CWL - MRS Commands */
+	reg = reg_read(REG_DFS_ADDR);
+	reg &= ~(REG_DFS_CL_NEXT_STATE_MASK << REG_DFS_CL_NEXT_STATE_OFFS);
+	reg &= ~(REG_DFS_CWL_NEXT_STATE_MASK << REG_DFS_CWL_NEXT_STATE_OFFS);
+
+	if (freq == DDR_400) {
+		if (dram_info->target_frequency == 0x8)
+			tmp = ddr3_cl_to_valid_cl(5);
+		else
+			tmp = ddr3_cl_to_valid_cl(6);
+	} else {
+		tmp = ddr3_cl_to_valid_cl(dram_info->cl);
+	}
+
+	/* [8] - DfsCLNextState */
+	reg |= ((tmp & REG_DFS_CL_NEXT_STATE_MASK) << REG_DFS_CL_NEXT_STATE_OFFS);
+	if (freq == DDR_400) {
+		/* [12] - DfsCWLNextState */
+		reg |= (((0) & REG_DFS_CWL_NEXT_STATE_MASK) <<
+			REG_DFS_CWL_NEXT_STATE_OFFS);
+	} else {
+		/* [12] - DfsCWLNextState */
+		reg |= (((dram_info->cwl) & REG_DFS_CWL_NEXT_STATE_MASK) <<
+			REG_DFS_CWL_NEXT_STATE_OFFS);
+	}
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Optional - Configure - DDR3_Rtt_nom_CS# */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = reg_read(REG_DDR3_MR1_CS_ADDR +
+				       (cs << MR_CS_ADDR_OFFS));
+			reg &= REG_DDR3_MR1_RTT_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+			dfs_reg_write(REG_DDR3_MR1_CS_ADDR +
+				      (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	/* Configure - Reset ADLLs - Set Reset */
+	reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) & REG_DRAM_PHY_CONFIG_MASK;
+	/* [31:30]] - reset pup data ctrl ADLL */
+	/* 0x15EC - DRAM PHY Config Register */
+	dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+	/* Configure - Reset ADLLs - Release Reset */
+	reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) | ~REG_DRAM_PHY_CONFIG_MASK;
+	/* [31:30] - normal pup data ctrl ADLL */
+	/* 0x15EC - DRAM PHY Config register */
+	dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+	/* Poll - Wait for APLL + ADLLs lock on new frequency */
+	do {
+		reg = reg_read(REG_PHY_LOCK_STATUS_ADDR) &
+			REG_PHY_LOCK_APLL_ADLL_STATUS_MASK;
+		/* 0x1674 [10:0] - Phy lock status Register */
+	} while (reg != REG_PHY_LOCK_APLL_ADLL_STATUS_MASK);
+
+	/* Configure - Reset the PHY SDR clock divider */
+	if (ratio_2to1) {
+		/* Pup Reset Divider B - Set Reset */
+		/* [28] - DataPupRdRST = 0 */
+		reg = reg_read(REG_SDRAM_CONFIG_ADDR) &
+			~(1 << REG_SDRAM_CONFIG_PUPRSTDIV_OFFS);
+		/* [28] - DataPupRdRST = 1 */
+		tmp = reg_read(REG_SDRAM_CONFIG_ADDR) |
+			(1 << REG_SDRAM_CONFIG_PUPRSTDIV_OFFS);
+		/* 0x1400 - SDRAM Configuration register */
+		dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+		/* Pup Reset Divider B - Release Reset */
+		/* 0x1400 - SDRAM Configuration register */
+		dfs_reg_write(REG_SDRAM_CONFIG_ADDR, tmp);
+	}
+
+	/* Configure - Reset the PHY Read FIFO and Write channels - Set Reset */
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK;
+	/* [30:29] = 0 - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/*
+	 * Configure - DRAM Data PHY Read [30], Write [29] path reset -
+	 * Release Reset
+	 */
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK;
+	/* [30:29] = '11' - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/* Registered DIMM support */
+	if (dram_info->reg_dimm) {
+		/*
+		 * Configure - Change register DRAM operating speed
+		 * (DDR3-1333 / DDR3-1600) - CWA_RC
+		 */
+		reg = (0xA & REG_SDRAM_OPERATION_CWA_RC_MASK) <<
+			REG_SDRAM_OPERATION_CWA_RC_OFFS;
+		if (freq <= DDR_400) {
+			/*
+			 * Configure - Change register DRAM operating speed
+			 * (DDR3-800) - CWA_DATA
+			 */
+			reg |= ((0x0 & REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+				REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+		} else if ((freq > DDR_400) && (freq <= DDR_533)) {
+			/*
+			 * Configure - Change register DRAM operating speed
+			 * (DDR3-1066) - CWA_DATA
+			 */
+			reg |= ((0x1 & REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+				REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+		} else if ((freq > DDR_533) && (freq <= DDR_666)) {
+			/*
+			 * Configure - Change register DRAM operating speed
+			 * (DDR3-1333) - CWA_DATA
+			 */
+			reg |= ((0x2 & REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+				REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+		} else {
+			/*
+			 * Configure - Change register DRAM operating speed
+			 * (DDR3-1600) - CWA_DATA
+			 */
+			reg |= ((0x3 & REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+				REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+		}
+
+		/* Configure - Set Delay - tSTAB */
+		reg |= (0x1 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS);
+		/* Configure - Issue CWA command with the above parameters */
+		reg |= (REG_SDRAM_OPERATION_CMD_CWA &
+			~(0xF << REG_SDRAM_OPERATION_CS_OFFS));
+
+		/* 0x1418 - SDRAM Operation Register */
+		dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+		/* Poll - Wait for CWA operation completion */
+		do {
+			reg = reg_read(REG_SDRAM_OPERATION_ADDR) &
+				REG_SDRAM_OPERATION_CMD_MASK;
+		} while (reg);
+	}
+
+	/* Configure - Exit Self Refresh */
+	/* [2] - DfsSR  */
+	reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Poll - DFS Register - 0x1528 [3] - DfsAtSR - All DRAM
+	 * devices on all ranks are NOT in self refresh mode
+	 */
+	do {
+		reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	/* Configure - Issue Refresh command */
+	/* [3-0] = 0x2 - Refresh Command, [11-8] - enabled Cs */
+	reg = REG_SDRAM_OPERATION_CMD_RFRS;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs))
+			reg &= ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+	}
+
+	/* 0x1418 - SDRAM Operation Register */
+	dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+	/* Poll - Wait for Refresh operation completion */
+	wait_refresh_op_complete();
+
+	/* Configure - Block new external transactions - Disable */
+	reg = reg_read(REG_DFS_ADDR);
+	reg &= ~(1 << REG_DFS_BLOCK_OFFS);	/* [1] - DfsBlock - Disable  */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Configure -  XBAR Retry response during Block to enable
+	 * internal access - Disable
+	 */
+	reg = reg_read(REG_METAL_MASK_ADDR);
+	/* [0] - RetryMask - Enable */
+	reg |= (1 << REG_METAL_MASK_RETRY_OFFS);
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			/* Configure - Set CL */
+			reg = reg_read(REG_DDR3_MR0_CS_ADDR +
+				       (cs << MR_CS_ADDR_OFFS)) &
+				~REG_DDR3_MR0_CL_MASK;
+			if (freq == DDR_400)
+				tmp = ddr3_cl_to_valid_cl(6);
+			else
+				tmp = ddr3_cl_to_valid_cl(dram_info->cl);
+			reg |= ((tmp & 0x1) << REG_DDR3_MR0_CL_OFFS);
+			reg |= ((tmp & 0xE) << REG_DDR3_MR0_CL_HIGH_OFFS);
+			dfs_reg_write(REG_DDR3_MR0_CS_ADDR +
+				      (cs << MR_CS_ADDR_OFFS), reg);
+
+			/* Configure - Set CWL */
+			reg = reg_read(REG_DDR3_MR2_CS_ADDR +
+				       (cs << MR_CS_ADDR_OFFS)) &
+				~(REG_DDR3_MR2_CWL_MASK << REG_DDR3_MR2_CWL_OFFS);
+			if (freq == DDR_400)
+				reg |= ((0) << REG_DDR3_MR2_CWL_OFFS);
+			else
+				reg |= ((dram_info->cwl) << REG_DDR3_MR2_CWL_OFFS);
+			dfs_reg_write(REG_DDR3_MR2_CS_ADDR +
+				      (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	DEBUG_DFS_C("DDR3 - DFS - Low To High - Ended successfuly - new Frequency - ",
+		    freq, 1);
+
+	return MV_OK;
+
+#else
+
+	/* This Flow is relevant for Armada370 A0 and ArmadaXP Z1 */
+
+	u32 reg, freq_par, tmp;
+	u32 cs = 0;
+
+	DEBUG_DFS_C("DDR3 - DFS - Low To High - Starting DFS procedure to Frequency - ",
+		    freq, 1);
+
+	/* target frequency - freq */
+	freq_par = ddr3_get_freq_parameter(freq, ratio_2to1);
+
+	reg = 0x0000FF00;
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg);
+
+	/* 0x1600 - PHY lock mask register */
+	reg = reg_read(REG_ODPG_CNTRL_ADDR);
+	reg |= (1 << REG_ODPG_CNTRL_OFFS);	/* [21] = 1 */
+	dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg);
+
+	/* 0x1670 - PHY lock mask register */
+	reg = reg_read(REG_PHY_LOCK_MASK_ADDR);
+	reg &= REG_PHY_LOCK_MASK_MASK;	/* [11:0] = 0 */
+	dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg);
+
+	/* Enable reconfig MR Registers after DFS */
+	reg = reg_read(REG_DFS_ADDR);	/* 0x1528 - DFS register */
+	/* [4] - Disable - reconfig MR registers after DFS_ERG */
+	reg &= ~0x11;
+	/* [0] - Enable - DRAM DLL after DFS */
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Disable DRAM Controller to crossbar retry */
+	/* [0] - disable */
+	reg = reg_read(REG_METAL_MASK_ADDR) & ~(1 << 0);
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	/* Enable DRAM Blocking */
+	/* [1] - DFS Block enable  */
+	reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_BLOCK_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Enable Self refresh */
+	/* [2] - DFS Self refresh enable  */
+	reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_SR_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Poll DFS Register - All DRAM devices on all ranks are in
+	 * self refresh mode - DFS can be executed afterwards
+	 */
+	/* 0x1528 [3] - DfsAtSR  */
+	do {
+		reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS);
+	} while (reg == 0x0);	/* Wait for '1' */
+
+	/*
+	 * Set Correct Ratio - if freq>MARGIN_FREQ use 2:1 ratio
+	 * else use 1:1 ratio
+	 */
+	if (ratio_2to1) {
+		/* [15] = 1 - Set 2:1 Ratio between Dunit and Phy */
+		reg = reg_read(REG_DDR_IO_ADDR) |
+			(1 << REG_DDR_IO_CLK_RATIO_OFFS);
+	} else {
+		/* [15] = 0 - Set 1:1 Ratio between Dunit and Phy */
+		reg = reg_read(REG_DDR_IO_ADDR) &
+			~(1 << REG_DDR_IO_CLK_RATIO_OFFS);
+	}
+	dfs_reg_write(REG_DDR_IO_ADDR, reg);	/* 0x1524 - DDR IO Register */
+
+	/* Switch HCLK Mux from (100Mhz) [16]=0, keep DFS request bit */
+	reg = 0x20040000;
+	/*
+	 * [29] - training logic request DFS, [28:27] -
+	 * preload patterns frequency [18]
+	 */
+
+	/* 0x18488 - DRAM Init control status register */
+	dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg);
+
+	/* Add delay between entering SR and start ratio modification */
+	udelay(1);
+
+	/*
+	 * Initial Setup - assure that the "load new ratio" is clear (bit 24)
+	 * and in the same chance, block reassertions of reset [15:8] and
+	 * force reserved bits[7:0].
+	 */
+	reg = 0x0000FFFF;
+	/* 0x18700 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	/*
+	 * RelaX whenever reset is asserted to that channel (good for any case)
+	 */
+	reg = 0x0000FF00;
+	/* 0x18704 - CPU Div CLK control 0 */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg);
+
+	reg = reg_read(REG_CPU_DIV_CLK_CTRL_3_ADDR) &
+		REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK;
+	reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS);
+	/* Full Integer ratio from PLL-out to ddr-clk */
+	/* 0x1870C - CPU Div CLK control 3 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_3_ADDR, reg);
+
+	/*
+	 * Shut off clock enable to the DDRPHY clock channel (this is the "D").
+	 * All the rest are kept as is (forced, but could be read-modify-write).
+	 * This is done now by RMW above.
+	 */
+
+	reg = 0x000FFF02;
+
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg);
+
+	/* Wait before replacing the clock on the DDR Phy Channel. */
+	udelay(1);
+
+	reg = 0x0102FDFF;
+	/*
+	 * This for triggering the frequency update. Bit[24] is the
+	 * central control
+	 * bits [23:16] == which channels to change ==2 ==> only DDR Phy
+	 *                 (smooth transition)
+	 * bits [15:8] == mask reset reassertion due to clock modification
+	 *                to these channels.
+	 * bits [7:0] == not in use
+	 */
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(1);
+
+	/*
+	 * Poll Div CLK status 0 register - indication that the clocks are
+	 * active - 0x18718 [8]
+	 */
+	do {
+		reg = reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR) &
+			(1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS);
+	} while (reg == 0);
+
+	reg = 0x000000FF;
+	/*
+	 * Clean the CTRL0, to be ready for next resets and next requests of
+	 * ratio modifications.
+	 */
+	/* 0x18700 - CPU Div CLK control 0 register */
+	dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg);
+
+	udelay(5);
+
+	if (ratio_2to1) {
+		/* Pup Reset Divider B - Set Reset */
+		/* [28] = 0 - Pup Reset Divider B */
+		reg = reg_read(REG_SDRAM_CONFIG_ADDR) & ~(1 << 28);
+		/* [28] = 1 - Pup Reset Divider B */
+		tmp = reg_read(REG_SDRAM_CONFIG_ADDR) | (1 << 28);
+		/* 0x1400 - SDRAM Configuration register */
+		dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+		/* Pup Reset Divider B - Release Reset */
+		/* 0x1400 - SDRAM Configuration register */
+		dfs_reg_write(REG_SDRAM_CONFIG_ADDR, tmp);
+	}
+
+	/* DRAM Data PHYs ADLL Reset - Set Reset */
+	reg = (reg_read(REG_DRAM_PHY_CONFIG_ADDR) & REG_DRAM_PHY_CONFIG_MASK);
+	/* [31:30]] - reset pup data ctrl ADLL */
+	/* 0x15EC - DRAM PHY Config Register */
+	dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+	udelay(25);
+
+	/* APLL lock indication - Poll Phy lock status Register - 0x1674 [9] */
+	do {
+		reg = reg_read(REG_PHY_LOCK_STATUS_ADDR) &
+			(1 << REG_PHY_LOCK_STATUS_LOCK_OFFS);
+	} while (reg == 0);
+
+	/* DRAM Data PHYs ADLL Reset - Release Reset */
+	reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) | ~REG_DRAM_PHY_CONFIG_MASK;
+	/* [31:30] - normal pup data ctrl ADLL */
+	/* 0x15EC - DRAM PHY Config register */
+	dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+	udelay(10000);		/* Wait 10msec */
+
+	/*
+	 * APLL lock indication - Poll Phy lock status Register - 0x1674 [11:0]
+	 */
+	do {
+		reg = reg_read(REG_PHY_LOCK_STATUS_ADDR) &
+			REG_PHY_LOCK_STATUS_LOCK_MASK;
+	} while (reg != REG_PHY_LOCK_STATUS_LOCK_MASK);
+
+	/* DRAM Data PHY Read [30], Write [29] path reset - Set Reset */
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK;
+	/* [30:29] = 0 - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/* DRAM Data PHY Read [30], Write [29] path reset - Release Reset */
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK;
+	/* [30:29] = '11' - Data Pup R/W path reset */
+	/* 0x1400 - SDRAM Configuration register */
+	dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/* Disable DFS Reconfig */
+	reg = reg_read(REG_DFS_ADDR) & ~(1 << 4);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* [2] - DFS Self refresh disable  */
+	reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/*
+	 * Poll DFS Register - 0x1528 [3] - DfsAtSR - All DRAM devices on
+	 * all ranks are NOT in self refresh mode
+	 */
+	do {
+		reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	/* 0x1404 */
+	reg = (reg_read(REG_DUNIT_CTRL_LOW_ADDR) & 0xFFFFFFE7) | 0x2;
+
+	/* Configure - 2T Mode - Restore original configuration */
+	/* [3:4] 2T - Restore value */
+	reg &= ~(REG_DUNIT_CTRL_LOW_2T_MASK << REG_DUNIT_CTRL_LOW_2T_OFFS);
+	reg |= ((dram_info->mode_2t & REG_DUNIT_CTRL_LOW_2T_MASK) <<
+		REG_DUNIT_CTRL_LOW_2T_OFFS);
+	dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+
+	udelay(1);		/* Wait 1us */
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = (reg_read(REG_DDR3_MR1_ADDR));
+			/* DLL Enable */
+			reg &= ~(1 << REG_DDR3_MR1_DLL_ENA_OFFS);
+			dfs_reg_write(REG_DDR3_MR1_ADDR, reg);
+
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			/* DLL Reset - MR0 */
+			reg = reg_read(REG_DDR3_MR0_ADDR);
+			dfs_reg_write(REG_DDR3_MR0_ADDR, reg);
+
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR0 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			reg = reg_read(REG_DDR3_MR0_ADDR);
+			reg &= ~0x74;	/* CL [3:0]; [6:4],[2] */
+
+			if (freq == DDR_400)
+				tmp = ddr3_cl_to_valid_cl(6) & 0xF;
+			else
+				tmp = ddr3_cl_to_valid_cl(dram_info->cl) & 0xF;
+
+			reg |= ((tmp & 0x1) << 2);
+			reg |= ((tmp >> 1) << 4);	/* to bit 4 */
+			dfs_reg_write(REG_DDR3_MR0_ADDR, reg);
+
+			reg = REG_SDRAM_OPERATION_CMD_MR0 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/* 0x1418 - SDRAM Operation Register */
+			dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			reg = reg_read(REG_DDR3_MR2_ADDR);
+			reg &= ~0x38;	/* CWL [5:3] */
+			/* CWL = 0 ,for 400 MHg is 5 */
+			if (freq != DDR_400)
+				reg |= dram_info->cwl << REG_DDR3_MR2_CWL_OFFS;
+			dfs_reg_write(REG_DDR3_MR2_ADDR, reg);
+			reg = REG_SDRAM_OPERATION_CMD_MR2 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/* 0x1418 - SDRAM Operation Register */
+			dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			/* Poll - Wait for Refresh operation completion */
+			wait_refresh_op_complete();
+
+			/* Set current rd_sample_delay  */
+			reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+			reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK <<
+				 (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			reg |= (dram_info->cl <<
+				(REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			dfs_reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg);
+
+			/* Set current rd_ready_delay  */
+			reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+			reg &= ~(REG_READ_DATA_READY_DELAYS_MASK <<
+				 (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+			reg |= ((dram_info->cl + 1) <<
+				(REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			dfs_reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+		}
+	}
+
+	/* Enable ODT on DLL-on mode */
+	dfs_reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, 0);
+
+	/* [1] - DFS Block disable  */
+	reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_BLOCK_OFFS);
+	dfs_reg_write(REG_DFS_ADDR, reg);	/* 0x1528 - DFS register */
+
+	/* Change DDR frequency to 100MHz procedure: */
+	/* 0x1600 - PHY lock mask register */
+	reg = reg_read(REG_ODPG_CNTRL_ADDR);
+	reg &= ~(1 << REG_ODPG_CNTRL_OFFS);	/* [21] = 0 */
+	dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg);
+
+	/* Change DDR frequency to 100MHz procedure: */
+	/* 0x1670 - PHY lock mask register */
+	reg = reg_read(REG_PHY_LOCK_MASK_ADDR);
+	reg |= ~REG_PHY_LOCK_MASK_MASK;	/* [11:0] = FFF */
+	dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg);
+
+	reg = reg_read(REG_METAL_MASK_ADDR) | (1 << 0);	/* [0] - disable */
+	/* 0x14B0 - Dunit MMask Register */
+	dfs_reg_write(REG_METAL_MASK_ADDR, reg);
+
+	DEBUG_DFS_C("DDR3 - DFS - Low To High - Ended successfuly - new Frequency - ",
+		    freq, 1);
+	return MV_OK;
+#endif
+}
diff --git a/drivers/ddr/marvell/axp/ddr3_dqs.c b/drivers/ddr/marvell/axp/ddr3_dqs.c
new file mode 100644
index 0000000..0a64dde
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_dqs.c
@@ -0,0 +1,1373 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_hw_training.h"
+
+/*
+ * Debug
+ */
+#define DEBUG_DQS_C(s, d, l) \
+	DEBUG_DQS_S(s); DEBUG_DQS_D(d, l); DEBUG_DQS_S("\n")
+#define DEBUG_DQS_FULL_C(s, d, l) \
+	DEBUG_DQS_FULL_S(s); DEBUG_DQS_FULL_D(d, l); DEBUG_DQS_FULL_S("\n")
+#define DEBUG_DQS_RESULTS_C(s, d, l) \
+	DEBUG_DQS_RESULTS_S(s); DEBUG_DQS_RESULTS_D(d, l); DEBUG_DQS_RESULTS_S("\n")
+#define DEBUG_PER_DQ_C(s, d, l) \
+	puts(s); printf("%x", d); puts("\n")
+
+#define DEBUG_DQS_RESULTS_S(s) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%s", s)
+#define DEBUG_DQS_RESULTS_D(d, l) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%x", d)
+
+#define DEBUG_PER_DQ_S(s) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_3, "%s", s)
+#define DEBUG_PER_DQ_D(d, l) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_3, "%x", d)
+#define DEBUG_PER_DQ_DD(d, l) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_3, "%d", d)
+
+#ifdef MV_DEBUG_DQS
+#define DEBUG_DQS_S(s)			puts(s)
+#define DEBUG_DQS_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_DQS_S(s)
+#define DEBUG_DQS_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_DQS_FULL
+#define DEBUG_DQS_FULL_S(s)		puts(s)
+#define DEBUG_DQS_FULL_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_DQS_FULL_S(s)
+#define DEBUG_DQS_FULL_D(d, l)
+#endif
+
+/* State machine for centralization - find low & high limit */
+enum {
+	PUP_ADLL_LIMITS_STATE_FAIL,
+	PUP_ADLL_LIMITS_STATE_PASS,
+	PUP_ADLL_LIMITS_STATE_FAIL_AFTER_PASS,
+};
+
+/* Hold centralization low results */
+static int centralization_low_limit[MAX_PUP_NUM] = { 0 };
+/* Hold centralization high results */
+static int centralization_high_limit[MAX_PUP_NUM] = { 0 };
+
+int ddr3_find_adll_limits(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, int is_tx);
+int ddr3_check_window_limits(u32 pup, int high_limit, int low_limit, int is_tx,
+			  int *size_valid);
+static int ddr3_center_calc(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+			    int is_tx);
+int ddr3_special_pattern_i_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+			      int is_tx, u32 special_pattern_pup);
+int ddr3_special_pattern_ii_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+				   int is_tx, u32 special_pattern_pup);
+int ddr3_set_dqs_centralization_results(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+				    int is_tx);
+
+#ifdef MV88F78X60
+extern u32 killer_pattern_32b[DQ_NUM][LEN_SPECIAL_PATTERN];
+extern u32 killer_pattern_64b[DQ_NUM][LEN_SPECIAL_PATTERN];
+extern int per_bit_data[MAX_PUP_NUM][DQ_NUM];
+#else
+extern u32 killer_pattern[DQ_NUM][LEN_16BIT_KILLER_PATTERN];
+extern u32 killer_pattern_32b[DQ_NUM][LEN_SPECIAL_PATTERN];
+#if defined(MV88F672X)
+extern int per_bit_data[MAX_PUP_NUM][DQ_NUM];
+#endif
+#endif
+extern u32 special_pattern[DQ_NUM][LEN_SPECIAL_PATTERN];
+
+static u32 *ddr3_dqs_choose_pattern(MV_DRAM_INFO *dram_info, u32 victim_dq)
+{
+	u32 *pattern_ptr;
+
+	/* Choose pattern */
+	switch (dram_info->ddr_width) {
+#if defined(MV88F672X)
+	case 16:
+		pattern_ptr = (u32 *)&killer_pattern[victim_dq];
+		break;
+#endif
+	case 32:
+		pattern_ptr = (u32 *)&killer_pattern_32b[victim_dq];
+		break;
+#if defined(MV88F78X60)
+	case 64:
+		pattern_ptr = (u32 *)&killer_pattern_64b[victim_dq];
+		break;
+#endif
+	default:
+#if defined(MV88F78X60)
+		pattern_ptr = (u32 *)&killer_pattern_32b[victim_dq];
+#else
+		pattern_ptr = (u32 *)&killer_pattern[victim_dq];
+#endif
+		break;
+	}
+
+	return pattern_ptr;
+}
+
+/*
+ * Name:     ddr3_dqs_centralization_rx
+ * Desc:     Execute the DQS centralization RX phase.
+ * Args:     dram_info
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_dqs_centralization_rx(MV_DRAM_INFO *dram_info)
+{
+	u32 cs, ecc, reg;
+	int status;
+
+	DEBUG_DQS_S("DDR3 - DQS Centralization RX - Starting procedure\n");
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_DQS_S("DDR3 - DQS Centralization RX - SW Override Enabled\n");
+
+	reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	/* Loop for each CS */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			DEBUG_DQS_FULL_C("DDR3 - DQS Centralization RX - CS - ",
+					 (u32) cs, 1);
+
+			for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) {
+
+				/* ECC Support - Switch ECC Mux on ecc=1 */
+				reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+					~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg |= (dram_info->ecc_ena *
+					ecc << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+				if (ecc)
+					DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - ECC Mux Enabled\n");
+				else
+					DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - ECC Mux Disabled\n");
+
+				DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - Find all limits\n");
+
+				status = ddr3_find_adll_limits(dram_info, cs,
+							       ecc, 0);
+				if (MV_OK != status)
+					return status;
+
+				DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - Start calculating center\n");
+
+				status = ddr3_center_calc(dram_info, cs, ecc,
+							  0);
+				if (MV_OK != status)
+					return status;
+			}
+		}
+	}
+
+	/* ECC Support - Disable ECC MUX */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+		~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
+		(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
+	reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_dqs_centralization_tx
+ * Desc:     Execute the DQS centralization TX phase.
+ * Args:     dram_info
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_dqs_centralization_tx(MV_DRAM_INFO *dram_info)
+{
+	u32 cs, ecc, reg;
+	int status;
+
+	DEBUG_DQS_S("DDR3 - DQS Centralization TX - Starting procedure\n");
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_DQS_S("DDR3 - DQS Centralization TX - SW Override Enabled\n");
+
+	reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	/* Loop for each CS */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			DEBUG_DQS_FULL_C("DDR3 - DQS Centralization TX - CS - ",
+					 (u32) cs, 1);
+			for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) {
+				/* ECC Support - Switch ECC Mux on ecc=1 */
+				reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+					~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg |= (dram_info->ecc_ena *
+					ecc << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+				if (ecc)
+					DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - ECC Mux Enabled\n");
+				else
+					DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - ECC Mux Disabled\n");
+
+				DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - Find all limits\n");
+
+				status = ddr3_find_adll_limits(dram_info, cs,
+							       ecc, 1);
+				if (MV_OK != status)
+					return status;
+
+				DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - Start calculating center\n");
+
+				status = ddr3_center_calc(dram_info, cs, ecc,
+							  1);
+				if (MV_OK != status)
+					return status;
+			}
+		}
+	}
+
+	/* ECC Support - Disable ECC MUX */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+		~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
+		(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
+	reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_find_adll_limits
+ * Desc:     Execute the Find ADLL limits phase.
+ * Args:     dram_info
+ *           cs
+ *           ecc_ena
+ *           is_tx             Indicate whether Rx or Tx
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_find_adll_limits(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, int is_tx)
+{
+	u32 victim_dq, pup, tmp;
+	u32 adll_addr;
+	u32 max_pup;		/* maximal pup index */
+	u32 pup_mask = 0;
+	u32 unlock_pup;		/* bit array of un locked pups */
+	u32 new_unlock_pup;	/* bit array of compare failed pups */
+	u32 curr_adll;
+	u32 adll_start_val;	/* adll start loop value - for rx or tx limit */
+	u32 high_limit;	/* holds found High Limit */
+	u32 low_limit;		/* holds found Low Limit */
+	int win_valid;
+	int update_win;
+	u32 sdram_offset;
+	u32 uj, cs_count, cs_tmp, ii;
+	u32 *pattern_ptr;
+	u32 dq;
+	u32 adll_end_val;	/* adll end of loop val - for rx or tx limit */
+	u8 analog_pbs[DQ_NUM][MAX_PUP_NUM][DQ_NUM][2];
+	u8 analog_pbs_sum[MAX_PUP_NUM][DQ_NUM][2];
+	int pup_adll_limit_state[MAX_PUP_NUM];	/* hold state of each pup */
+
+	adll_addr = ((is_tx == 1) ? PUP_DQS_WR : PUP_DQS_RD);
+	adll_end_val = ((is_tx == 1) ? ADLL_MIN : ADLL_MAX);
+	adll_start_val = ((is_tx == 1) ? ADLL_MAX : ADLL_MIN);
+	max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups);
+
+	DEBUG_DQS_FULL_S("DDR3 - DQS Find Limits - Starting Find ADLL Limits\n");
+
+	/* init the array */
+	for (pup = 0; pup < max_pup; pup++) {
+		centralization_low_limit[pup] = ADLL_MIN;
+		centralization_high_limit[pup] = ADLL_MAX;
+	}
+
+	/* Killer Pattern */
+	cs_count = 0;
+	for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) {
+		if (dram_info->cs_ena & (1 << cs_tmp))
+			cs_count++;
+	}
+	sdram_offset = cs_count * (SDRAM_CS_SIZE + 1);
+	sdram_offset += ((is_tx == 1) ?
+			 SDRAM_DQS_TX_OFFS : SDRAM_DQS_RX_OFFS);
+
+	/* Prepare pup masks */
+	for (pup = 0; pup < max_pup; pup++)
+		pup_mask |= (1 << pup);
+
+	for (pup = 0; pup < max_pup; pup++) {
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			analog_pbs_sum[pup][dq][0] = adll_start_val;
+			analog_pbs_sum[pup][dq][1] = adll_end_val;
+		}
+	}
+
+	/* Loop - use different pattern for each victim_dq */
+	for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+		DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - Victim DQ - ",
+				 (u32)victim_dq, 1);
+		/*
+		 * The pups 3 bit arrays represent state machine. with
+		 * 3 stages for each pup.
+		 * 1. fail and didn't get pass in earlier compares.
+		 * 2. pass compare
+		 * 3. fail after pass - end state.
+		 * The window limits are the adll values where the adll
+		 * was in the pass stage.
+		 */
+
+		/* Set all states to Fail (1st state) */
+		for (pup = 0; pup < max_pup; pup++)
+			pup_adll_limit_state[pup] = PUP_ADLL_LIMITS_STATE_FAIL;
+
+		/* Set current valid pups */
+		unlock_pup = pup_mask;
+
+		/* Set ADLL to start value */
+		curr_adll = adll_start_val;
+
+#if defined(MV88F78X60)
+		for (pup = 0; pup < max_pup; pup++) {
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				analog_pbs[victim_dq][pup][dq][0] =
+					adll_start_val;
+				analog_pbs[victim_dq][pup][dq][1] =
+					adll_end_val;
+				per_bit_data[pup][dq] = 0;
+			}
+		}
+#endif
+
+		for (uj = 0; uj < ADLL_MAX; uj++) {
+			DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - Setting ADLL to ",
+					 curr_adll, 2);
+			for (pup = 0; pup < max_pup; pup++) {
+				if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
+					tmp = ((is_tx == 1) ? curr_adll +
+					       dram_info->wl_val[cs]
+					       [pup * (1 - ecc) + ecc * ECC_PUP]
+					       [D] : curr_adll);
+					ddr3_write_pup_reg(adll_addr, cs, pup +
+						(ecc * ECC_PUP), 0, tmp);
+				}
+			}
+
+			/* Choose pattern */
+			pattern_ptr = ddr3_dqs_choose_pattern(dram_info,
+							      victim_dq);
+
+			/* '1' - means pup failed, '0' - means pup pass */
+			new_unlock_pup = 0;
+
+			/* Read and compare results for Victim_DQ# */
+			for (ii = 0; ii < 3; ii++) {
+				u32 tmp = 0;
+				if (MV_OK != ddr3_sdram_dqs_compare(dram_info,
+							   unlock_pup, &tmp,
+							   pattern_ptr,
+							   LEN_KILLER_PATTERN,
+							   sdram_offset +
+							   LEN_KILLER_PATTERN *
+							   4 * victim_dq,
+							   is_tx, 0, NULL,
+							   0))
+					return MV_DDR3_TRAINING_ERR_DRAM_COMPARE;
+
+				new_unlock_pup |= tmp;
+			}
+
+			pup = 0;
+			DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - UnlockPup: ",
+					 unlock_pup, 2);
+			DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - NewUnlockPup: ",
+					 new_unlock_pup, 2);
+
+			/* Update pup state */
+			for (pup = 0; pup < max_pup; pup++) {
+				if (IS_PUP_ACTIVE(unlock_pup, pup) == 0) {
+					DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - Skipping pup ",
+							 pup, 1);
+					continue;
+				}
+
+				/*
+				 * Still didn't find the window limit of the pup
+				 */
+				if (IS_PUP_ACTIVE(new_unlock_pup, pup) == 1) {
+					/* Current compare result == fail */
+					if (pup_adll_limit_state[pup] ==
+					    PUP_ADLL_LIMITS_STATE_PASS) {
+						/*
+						 * If now it failed but passed
+						 * earlier
+						 */
+						DEBUG_DQS_S("DDR3 - DQS Find Limits - PASS to FAIL: CS - ");
+						DEBUG_DQS_D(cs, 1);
+						DEBUG_DQS_S(", DQ - ");
+						DEBUG_DQS_D(victim_dq, 1);
+						DEBUG_DQS_S(", Pup - ");
+						DEBUG_DQS_D(pup, 1);
+						DEBUG_DQS_S(", ADLL - ");
+						DEBUG_DQS_D(curr_adll, 2);
+						DEBUG_DQS_S("\n");
+
+#if defined(MV88F78X60)
+						for (dq = 0; dq < DQ_NUM; dq++) {
+							if ((analog_pbs[victim_dq][pup][dq][0] != adll_start_val)
+							    && (analog_pbs[victim_dq][pup]
+								[dq][1] == adll_end_val))
+								analog_pbs
+									[victim_dq]
+									[pup][dq]
+									[1] =
+									curr_adll;
+						}
+#endif
+						win_valid = 1;
+						update_win = 0;
+
+						/* Keep min / max limit value */
+						if (is_tx == 0) {
+							/* RX - found upper limit */
+							if (centralization_high_limit[pup] >
+							    (curr_adll - 1)) {
+								high_limit =
+									curr_adll - 1;
+								low_limit =
+									centralization_low_limit[pup];
+								update_win = 1;
+							}
+						} else {
+							/* TX - found lower limit */
+							if (centralization_low_limit[pup] < (curr_adll + 1)) {
+								high_limit =
+									centralization_high_limit
+									[pup];
+								low_limit =
+									curr_adll + 1;
+								update_win =
+									1;
+							}
+						}
+
+						if (update_win == 1) {
+							/*
+							 * Before updating
+							 * window limits we need
+							 * to check that the
+							 * limits are valid
+							 */
+							if (MV_OK !=
+							    ddr3_check_window_limits
+							    (pup, high_limit,
+							     low_limit, is_tx,
+							     &win_valid))
+								return MV_DDR3_TRAINING_ERR_WIN_LIMITS;
+
+							if (win_valid == 1) {
+								/*
+								 * Window limits
+								 * should be
+								 * updated
+								 */
+								centralization_low_limit
+									[pup] =
+									low_limit;
+								centralization_high_limit
+									[pup] =
+									high_limit;
+							}
+						}
+
+						if (win_valid == 1) {
+							/* Found end of window - lock the pup */
+							pup_adll_limit_state[pup] =
+								PUP_ADLL_LIMITS_STATE_FAIL_AFTER_PASS;
+							unlock_pup &= ~(1 << pup);
+						} else {
+							/* Probably false pass - reset status */
+							pup_adll_limit_state[pup] =
+								PUP_ADLL_LIMITS_STATE_FAIL;
+
+#if defined(MV88F78X60)
+							/* Clear logging array of win size (per Dq) */
+							for (dq = 0;
+							     dq < DQ_NUM;
+							     dq++) {
+								analog_pbs
+									[victim_dq]
+									[pup][dq]
+									[0] =
+									adll_start_val;
+								analog_pbs
+									[victim_dq]
+									[pup][dq]
+									[1] =
+									adll_end_val;
+								per_bit_data
+									[pup][dq]
+									= 0;
+							}
+#endif
+						}
+					}
+				} else {
+					/* Current compare result == pass */
+					if (pup_adll_limit_state[pup] ==
+					    PUP_ADLL_LIMITS_STATE_FAIL) {
+						/* If now it passed but failed earlier */
+						DEBUG_DQS_S("DDR3 - DQS Find Limits - FAIL to PASS: CS - ");
+						DEBUG_DQS_D(cs, 1);
+						DEBUG_DQS_S(", DQ - ");
+						DEBUG_DQS_D(victim_dq, 1);
+						DEBUG_DQS_S(", Pup - ");
+						DEBUG_DQS_D(pup, 1);
+						DEBUG_DQS_S(", ADLL - ");
+						DEBUG_DQS_D(curr_adll, 2);
+						DEBUG_DQS_S("\n");
+
+#if defined(MV88F78X60)
+						for (dq = 0; dq < DQ_NUM;
+						     dq++) {
+							if (analog_pbs[victim_dq][pup][dq][0] == adll_start_val)
+								analog_pbs
+								    [victim_dq]
+								    [pup][dq]
+								    [0] =
+								    curr_adll;
+						}
+#endif
+						/* Found start of window */
+						pup_adll_limit_state[pup] =
+						    PUP_ADLL_LIMITS_STATE_PASS;
+
+						/* Keep min / max limit value */
+						if (is_tx == 0) {
+							/* RX - found low limit */
+							if (centralization_low_limit[pup] <= curr_adll)
+								centralization_low_limit
+								    [pup] =
+								    curr_adll;
+						} else {
+							/* TX - found high limit */
+							if (centralization_high_limit[pup] >= curr_adll)
+								centralization_high_limit
+								    [pup] =
+								    curr_adll;
+						}
+					}
+				}
+			}
+
+			if (unlock_pup == 0) {
+				/* Found limit to all pups */
+				DEBUG_DQS_FULL_S("DDR3 - DQS Find Limits - found PUP limit\n");
+				break;
+			}
+
+			/*
+			 * Increment / decrement (Move to right / left
+			 * one phase - ADLL) dqs RX / TX delay (for all un
+			 * lock pups
+			 */
+			if (is_tx == 0)
+				curr_adll++;
+			else
+				curr_adll--;
+		}
+
+		if (unlock_pup != 0) {
+			/*
+			 * Found pups that didn't reach to the end of the
+			 * state machine
+			 */
+			DEBUG_DQS_C("DDR3 - DQS Find Limits - Pups that didn't reached end of the state machine: ",
+				    unlock_pup, 1);
+
+			for (pup = 0; pup < max_pup; pup++) {
+				if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
+					if (pup_adll_limit_state[pup] ==
+					    PUP_ADLL_LIMITS_STATE_FAIL) {
+						/* ERROR - found fail for all window size */
+						DEBUG_DQS_S("DDR3 - DQS Find Limits - Got FAIL for the complete range on pup - ");
+						DEBUG_DQS_D(pup, 1);
+						DEBUG_DQS_C(" victim DQ ",
+							    victim_dq, 1);
+
+						/* For debug - set min limit to illegal limit */
+						centralization_low_limit[pup]
+							= ADLL_ERROR;
+						/*
+						 * In case the pup is in mode
+						 * PASS - the limit is the min
+						 * / max adll, no need to
+						 * update because of the results
+						 * array default value
+						 */
+						return MV_DDR3_TRAINING_ERR_PUP_RANGE;
+					}
+				}
+			}
+		}
+	}
+
+	DEBUG_DQS_S("DDR3 - DQS Find Limits - DQ values per victim results:\n");
+	for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+		for (pup = 0; pup < max_pup; pup++) {
+			DEBUG_DQS_S("Victim DQ-");
+			DEBUG_DQS_D(victim_dq, 1);
+			DEBUG_DQS_S(", PUP-");
+			DEBUG_DQS_D(pup, 1);
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				DEBUG_DQS_S(", DQ-");
+				DEBUG_DQS_D(dq, 1);
+				DEBUG_DQS_S(",S-");
+				DEBUG_DQS_D(analog_pbs[victim_dq][pup][dq]
+					    [0], 2);
+				DEBUG_DQS_S(",E-");
+				DEBUG_DQS_D(analog_pbs[victim_dq][pup][dq]
+					    [1], 2);
+
+				if (is_tx == 0) {
+					if (analog_pbs[victim_dq][pup][dq][0]
+					    > analog_pbs_sum[pup][dq][0])
+						analog_pbs_sum[pup][dq][0] =
+						    analog_pbs[victim_dq][pup]
+						    [dq][0];
+					if (analog_pbs[victim_dq][pup][dq][1]
+					    < analog_pbs_sum[pup][dq][1])
+						analog_pbs_sum[pup][dq][1] =
+						    analog_pbs[victim_dq][pup]
+						    [dq][1];
+				} else {
+					if (analog_pbs[victim_dq][pup][dq][0]
+					    < analog_pbs_sum[pup][dq][0])
+						analog_pbs_sum[pup][dq][0] =
+						    analog_pbs[victim_dq][pup]
+						    [dq][0];
+					if (analog_pbs[victim_dq][pup][dq][1]
+					    > analog_pbs_sum[pup][dq][1])
+						analog_pbs_sum[pup][dq][1] =
+						    analog_pbs[victim_dq][pup]
+						    [dq][1];
+				}
+			}
+			DEBUG_DQS_S("\n");
+		}
+	}
+
+	if (ddr3_get_log_level() >= MV_LOG_LEVEL_3) {
+		u32 dq;
+
+		DEBUG_PER_DQ_S("\n########## LOG LEVEL 3(Windows margins per-DQ) ##########\n");
+		if (is_tx) {
+			DEBUG_PER_DQ_C("DDR3 - TX  CS: ", cs, 1);
+		} else {
+			DEBUG_PER_DQ_C("DDR3 - RX  CS: ", cs, 1);
+		}
+
+		if (ecc == 0) {
+			DEBUG_PER_DQ_S("\n DATA RESULTS:\n");
+		} else {
+			DEBUG_PER_DQ_S("\n ECC RESULTS:\n");
+		}
+
+		/* Since all dq has the same value we take 0 as representive */
+		dq = 0;
+		for (pup = 0; pup < max_pup; pup++) {
+			if (ecc == 0) {
+				DEBUG_PER_DQ_S("\nBYTE:");
+				DEBUG_PER_DQ_D(pup, 1);
+				DEBUG_PER_DQ_S("\n");
+			} else {
+				DEBUG_PER_DQ_S("\nECC BYTE:\n");
+			}
+			DEBUG_PER_DQ_S("  DQ's        LOW       HIGH       WIN-SIZE\n");
+			DEBUG_PER_DQ_S("============================================\n");
+			for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+				if (ecc == 0) {
+					DEBUG_PER_DQ_S("DQ[");
+					DEBUG_PER_DQ_DD((victim_dq +
+							 DQ_NUM * pup), 2);
+					DEBUG_PER_DQ_S("]");
+				} else {
+					DEBUG_PER_DQ_S("CB[");
+					DEBUG_PER_DQ_DD(victim_dq, 2);
+					DEBUG_PER_DQ_S("]");
+				}
+				if (is_tx) {
+					DEBUG_PER_DQ_S("      0x");
+					DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][1], 2);	/* low value */
+					DEBUG_PER_DQ_S("        0x");
+					DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][0], 2);	/* high value */
+					DEBUG_PER_DQ_S("        0x");
+					DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][0] - analog_pbs[victim_dq][pup][dq][1], 2);	/* win-size */
+				} else {
+					DEBUG_PER_DQ_S("     0x");
+					DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][0], 2);	/* low value */
+					DEBUG_PER_DQ_S("       0x");
+					DEBUG_PER_DQ_D((analog_pbs[victim_dq][pup][dq][1] - 1), 2);	/* high value */
+					DEBUG_PER_DQ_S("       0x");
+					DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][1] - analog_pbs[victim_dq][pup][dq][0], 2);	/* win-size */
+				}
+				DEBUG_PER_DQ_S("\n");
+			}
+		}
+		DEBUG_PER_DQ_S("\n");
+	}
+
+	if (is_tx) {
+		DEBUG_DQS_S("DDR3 - DQS TX - Find Limits - DQ values Summary:\n");
+	} else {
+		DEBUG_DQS_S("DDR3 - DQS RX - Find Limits - DQ values Summary:\n");
+	}
+
+	for (pup = 0; pup < max_pup; pup++) {
+		DEBUG_DQS_S("PUP-");
+		DEBUG_DQS_D(pup, 1);
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			DEBUG_DQS_S(", DQ-");
+			DEBUG_DQS_D(dq, 1);
+			DEBUG_DQS_S(",S-");
+			DEBUG_DQS_D(analog_pbs_sum[pup][dq][0], 2);
+			DEBUG_DQS_S(",E-");
+			DEBUG_DQS_D(analog_pbs_sum[pup][dq][1], 2);
+		}
+		DEBUG_DQS_S("\n");
+	}
+
+	if (is_tx) {
+		DEBUG_DQS_S("DDR3 - DQS TX - Find Limits - DQ values Summary:\n");
+	} else {
+		DEBUG_DQS_S("DDR3 - DQS RX - Find Limits - DQ values Summary:\n");
+	}
+
+	for (pup = 0; pup < max_pup; pup++) {
+		if (max_pup == 1) {
+			/* For ECC PUP */
+			DEBUG_DQS_S("DDR3 - DQS8");
+		} else {
+			DEBUG_DQS_S("DDR3 - DQS");
+			DEBUG_DQS_D(pup, 1);
+		}
+
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			DEBUG_DQS_S(", DQ-");
+			DEBUG_DQS_D(dq, 1);
+			DEBUG_DQS_S("::S-");
+			DEBUG_DQS_D(analog_pbs_sum[pup][dq][0], 2);
+			DEBUG_DQS_S(",E-");
+			DEBUG_DQS_D(analog_pbs_sum[pup][dq][1], 2);
+		}
+		DEBUG_DQS_S("\n");
+	}
+
+	DEBUG_DQS_S("DDR3 - DQS Find Limits - Ended\n");
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_check_window_limits
+ * Desc:     Check window High & Low limits.
+ * Args:     pup                pup index
+ *           high_limit           window high limit
+ *           low_limit            window low limit
+ *           is_tx                Indicate whether Rx or Tx
+ *           size_valid          Indicate whether window size is valid
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_check_window_limits(u32 pup, int high_limit, int low_limit, int is_tx,
+			     int *size_valid)
+{
+	DEBUG_DQS_FULL_S("DDR3 - DQS Check Win Limits - Starting\n");
+
+	if (low_limit > high_limit) {
+		DEBUG_DQS_S("DDR3 - DQS Check Win Limits - Pup ");
+		DEBUG_DQS_D(pup, 1);
+		DEBUG_DQS_S(" Low Limit grater than High Limit\n");
+		*size_valid = 0;
+		return MV_OK;
+	}
+
+	/*
+	 * Check that window size is valid, if not it was probably false pass
+	 * before
+	 */
+	if ((high_limit - low_limit) < MIN_WIN_SIZE) {
+		/*
+		 * Since window size is too small probably there was false
+		 * pass
+		 */
+		*size_valid = 0;
+
+		DEBUG_DQS_S("DDR3 - DQS Check Win Limits - Pup ");
+		DEBUG_DQS_D(pup, 1);
+		DEBUG_DQS_S(" Window size is smaller than MIN_WIN_SIZE\n");
+
+	} else if ((high_limit - low_limit) > ADLL_MAX) {
+		*size_valid = 0;
+
+		DEBUG_DQS_S("DDR3 - DQS Check Win Limits - Pup ");
+		DEBUG_DQS_D(pup, 1);
+		DEBUG_DQS_S
+		    (" Window size is bigger than max ADLL taps (31)  Exiting.\n");
+
+		return MV_FAIL;
+
+	} else {
+		*size_valid = 1;
+
+		DEBUG_DQS_FULL_S("DDR3 - DQS Check Win Limits - Pup ");
+		DEBUG_DQS_FULL_D(pup, 1);
+		DEBUG_DQS_FULL_C(" window size is ", (high_limit - low_limit),
+				 2);
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_center_calc
+ * Desc:     Execute the calculate the center of windows phase.
+ * Args:     pDram Info
+ *           is_tx             Indicate whether Rx or Tx
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static int ddr3_center_calc(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+			    int is_tx)
+{
+	/* bit array of pups that need specail search */
+	u32 special_pattern_i_pup = 0;
+	u32 special_pattern_ii_pup = 0;
+	u32 pup;
+	u32 max_pup;
+
+	max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups);
+
+	for (pup = 0; pup < max_pup; pup++) {
+		if (is_tx == 0) {
+			/* Check special pattern I */
+			/*
+			 * Special pattern Low limit search - relevant only
+			 * for Rx, win size < threshold and low limit = 0
+			 */
+			if (((centralization_high_limit[pup] -
+			      centralization_low_limit[pup]) < VALID_WIN_THRS)
+			    && (centralization_low_limit[pup] == MIN_DELAY))
+				special_pattern_i_pup |= (1 << pup);
+
+			/* Check special pattern II */
+			/*
+			 * Special pattern High limit search - relevant only
+			 * for Rx, win size < threshold and high limit = 31
+			 */
+			if (((centralization_high_limit[pup] -
+			      centralization_low_limit[pup]) < VALID_WIN_THRS)
+			    && (centralization_high_limit[pup] == MAX_DELAY))
+				special_pattern_ii_pup |= (1 << pup);
+		}
+	}
+
+	/* Run special pattern Low limit search - for relevant pup */
+	if (special_pattern_i_pup != 0) {
+		DEBUG_DQS_S("DDR3 - DQS Center Calc - Entering special pattern I for Low limit search\n");
+		if (MV_OK !=
+		    ddr3_special_pattern_i_search(dram_info, cs, ecc, is_tx,
+					      special_pattern_i_pup))
+			return MV_DDR3_TRAINING_ERR_DQS_LOW_LIMIT_SEARCH;
+	}
+
+	/* Run special pattern High limit search - for relevant pup */
+	if (special_pattern_ii_pup != 0) {
+		DEBUG_DQS_S("DDR3 - DQS Center Calc - Entering special pattern II for High limit search\n");
+		if (MV_OK !=
+		    ddr3_special_pattern_ii_search(dram_info, cs, ecc, is_tx,
+						   special_pattern_ii_pup))
+			return MV_DDR3_TRAINING_ERR_DQS_HIGH_LIMIT_SEARCH;
+	}
+
+	/* Set adll to center = (General_High_limit + General_Low_limit)/2 */
+	return ddr3_set_dqs_centralization_results(dram_info, cs, ecc, is_tx);
+}
+
+/*
+ * Name:     ddr3_special_pattern_i_search
+ * Desc:     Execute special pattern low limit search.
+ * Args:
+ *           special_pattern_pup  The pups that need the special search
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_special_pattern_i_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+				  int is_tx, u32 special_pattern_pup)
+{
+	u32 victim_dq;		/* loop index - victim DQ */
+	u32 adll_idx;
+	u32 pup;
+	u32 unlock_pup;		/* bit array of the unlock pups  */
+	u32 first_fail;	/* bit array - of pups that  get first fail */
+	u32 new_lockup_pup;	/* bit array of compare failed pups */
+	u32 pass_pup;		/* bit array of compare pass pup */
+	u32 sdram_offset;
+	u32 max_pup;
+	u32 comp_val;
+	u32 special_res[MAX_PUP_NUM];	/* hold tmp results */
+
+	DEBUG_DQS_S("DDR3 - DQS - Special Pattern I Search - Starting\n");
+
+	max_pup = ecc + (1 - ecc) * dram_info->num_of_std_pups;
+
+	/* Init the temporary results to max ADLL value */
+	for (pup = 0; pup < max_pup; pup++)
+		special_res[pup] = ADLL_MAX;
+
+	/* Run special pattern for all DQ - use the same pattern */
+	for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+		unlock_pup = special_pattern_pup;
+		first_fail = 0;
+
+		sdram_offset = cs * SDRAM_CS_SIZE + SDRAM_DQS_RX_OFFS +
+			LEN_KILLER_PATTERN * 4 * victim_dq;
+
+		for (pup = 0; pup < max_pup; pup++) {
+			/* Set adll value per PUP. adll = high limit per pup */
+			if (IS_PUP_ACTIVE(unlock_pup, pup)) {
+				/* only for pups that need special search */
+				ddr3_write_pup_reg(PUP_DQS_RD, cs,
+						   pup + (ecc * ECC_PUP), 0,
+						   centralization_high_limit
+						   [pup]);
+			}
+		}
+
+		adll_idx = 0;
+		do {
+			/*
+			 * Perform read and compare simultaneously for all
+			 * un-locked MC use the special pattern mask
+			 */
+			new_lockup_pup = 0;
+
+			if (MV_OK !=
+			    ddr3_sdram_dqs_compare(dram_info, unlock_pup,
+						   &new_lockup_pup,
+						   special_pattern
+						   [victim_dq],
+						   LEN_SPECIAL_PATTERN,
+						   sdram_offset, 0,
+						   0, NULL, 1))
+				return MV_FAIL;
+
+			DEBUG_DQS_S("DDR3 - DQS - Special I - ADLL value is: ");
+			DEBUG_DQS_D(adll_idx, 2);
+			DEBUG_DQS_S(", UnlockPup: ");
+			DEBUG_DQS_D(unlock_pup, 2);
+			DEBUG_DQS_S(", NewLockPup: ");
+			DEBUG_DQS_D(new_lockup_pup, 2);
+			DEBUG_DQS_S("\n");
+
+			if (unlock_pup != new_lockup_pup)
+				DEBUG_DQS_S("DDR3 - DQS - Special I - Some Pup passed!\n");
+
+			/* Search for pups with passed compare & already fail */
+			pass_pup = first_fail & ~new_lockup_pup & unlock_pup;
+			first_fail |= new_lockup_pup;
+			unlock_pup &= ~pass_pup;
+
+			/* Get pass pups */
+			if (pass_pup != 0) {
+				for (pup = 0; pup < max_pup; pup++) {
+					if (IS_PUP_ACTIVE(pass_pup, pup) ==
+					    1) {
+						/* If pup passed and has first fail = 1 */
+						/* keep min value of ADLL max value - current adll */
+						/* (centralization_high_limit[pup] + adll_idx) = current adll !!! */
+						comp_val =
+						    (ADLL_MAX -
+						     (centralization_high_limit
+						      [pup] + adll_idx));
+
+						DEBUG_DQS_C
+						    ("DDR3 - DQS - Special I - Pup - ",
+						     pup, 1);
+						DEBUG_DQS_C
+						    (" comp_val = ",
+						     comp_val, 2);
+
+						if (comp_val <
+						    special_res[pup]) {
+							special_res[pup] =
+							    comp_val;
+							centralization_low_limit
+							    [pup] =
+							    (-1) *
+							    comp_val;
+
+							DEBUG_DQS_C
+							    ("DDR3 - DQS - Special I - Pup - ",
+							     pup, 1);
+							DEBUG_DQS_C
+							    (" Changed Low limit to ",
+							     centralization_low_limit
+							     [pup], 2);
+						}
+					}
+				}
+			}
+
+			/*
+			 * Did all PUP found missing window?
+			 * Check for each pup if adll (different for each pup)
+			 * reach maximum if reach max value - lock the pup
+			 * if not - increment (Move to right one phase - ADLL)
+			 * dqs RX delay
+			 */
+			adll_idx++;
+			for (pup = 0; pup < max_pup; pup++) {
+				if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
+					/* Check only unlocked pups */
+					if ((centralization_high_limit[pup] +
+					     adll_idx) >= ADLL_MAX) {
+						/* reach maximum - lock the pup */
+						DEBUG_DQS_C("DDR3 - DQS - Special I - reach maximum - lock pup ",
+							    pup, 1);
+						unlock_pup &= ~(1 << pup);
+					} else {
+						/* Didn't reach maximum - increment ADLL */
+						ddr3_write_pup_reg(PUP_DQS_RD,
+								   cs,
+								   pup +
+								   (ecc *
+								    ECC_PUP), 0,
+								   (centralization_high_limit
+								    [pup] +
+								    adll_idx));
+					}
+				}
+			}
+		} while (unlock_pup != 0);
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_special_pattern_ii_search
+ * Desc:     Execute special pattern high limit search.
+ * Args:
+ *           special_pattern_pup  The pups that need the special search
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_special_pattern_ii_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc,
+				   int is_tx, u32 special_pattern_pup)
+{
+	u32 victim_dq;		/* loop index - victim DQ */
+	u32 adll_idx;
+	u32 pup;
+	u32 unlock_pup;		/* bit array of the unlock pups  */
+	u32 first_fail;	/* bit array - of pups that  get first fail */
+	u32 new_lockup_pup;	/* bit array of compare failed pups */
+	u32 pass_pup;		/* bit array of compare pass pup */
+	u32 sdram_offset;
+	u32 max_pup;
+	u32 comp_val;
+	u32 special_res[MAX_PUP_NUM];	/* hold tmp results */
+
+	DEBUG_DQS_S("DDR3 - DQS - Special Pattern II Search - Starting\n");
+
+	max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups);
+
+	/* init the tmporary results to max ADLL value */
+	for (pup = 0; pup < max_pup; pup++)
+		special_res[pup] = ADLL_MAX;
+
+	sdram_offset = cs * SDRAM_CS_SIZE + SDRAM_DQS_RX_OFFS;
+
+	/* run special pattern for all DQ - use the same pattern */
+	for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+		unlock_pup = special_pattern_pup;
+		first_fail = 0;
+
+		for (pup = 0; pup < max_pup; pup++) {
+			/* Set adll value per PUP. adll = 0 */
+			if (IS_PUP_ACTIVE(unlock_pup, pup)) {
+				/* Only for pups that need special search */
+				ddr3_write_pup_reg(PUP_DQS_RD, cs,
+						   pup + (ecc * ECC_PUP), 0,
+						   ADLL_MIN);
+			}
+		}
+
+		adll_idx = 0;
+		do {
+			/*
+			 * Perform read and compare simultaneously for all
+			 * un-locked MC use the special pattern mask
+			 */
+			new_lockup_pup = 0;
+
+			if (MV_OK != ddr3_sdram_dqs_compare(
+				    dram_info, unlock_pup, &new_lockup_pup,
+				    special_pattern[victim_dq],
+				    LEN_SPECIAL_PATTERN,
+				    sdram_offset, 0, 0, NULL, 0))
+				return MV_FAIL;
+
+			DEBUG_DQS_S("DDR3 - DQS - Special II - ADLL value is ");
+			DEBUG_DQS_D(adll_idx, 2);
+			DEBUG_DQS_S("unlock_pup ");
+			DEBUG_DQS_D(unlock_pup, 1);
+			DEBUG_DQS_S("new_lockup_pup ");
+			DEBUG_DQS_D(new_lockup_pup, 1);
+			DEBUG_DQS_S("\n");
+
+			if (unlock_pup != new_lockup_pup) {
+				DEBUG_DQS_S("DDR3 - DQS - Special II - Some Pup passed!\n");
+			}
+
+			/* Search for pups with passed compare & already fail */
+			pass_pup = first_fail & ~new_lockup_pup & unlock_pup;
+			first_fail |= new_lockup_pup;
+			unlock_pup &= ~pass_pup;
+
+			/* Get pass pups */
+			if (pass_pup != 0) {
+				for (pup = 0; pup < max_pup; pup++) {
+					if (IS_PUP_ACTIVE(pass_pup, pup) ==
+					    1) {
+						/* If pup passed and has first fail = 1 */
+						/* keep min value of ADLL max value - current adll */
+						/* (adll_idx) = current adll !!! */
+						comp_val = adll_idx;
+
+						DEBUG_DQS_C("DDR3 - DQS - Special II - Pup - ",
+							    pup, 1);
+						DEBUG_DQS_C(" comp_val = ",
+							    comp_val, 1);
+
+						if (comp_val <
+						    special_res[pup]) {
+							special_res[pup] =
+							    comp_val;
+							centralization_high_limit
+							    [pup] =
+							    ADLL_MAX +
+							    comp_val;
+
+							DEBUG_DQS_C
+							    ("DDR3 - DQS - Special II - Pup - ",
+							     pup, 1);
+							DEBUG_DQS_C
+							    (" Changed High limit to ",
+							     centralization_high_limit
+							     [pup], 2);
+						}
+					}
+				}
+			}
+
+			/*
+			 * Did all PUP found missing window?
+			 * Check for each pup if adll (different for each pup)
+			 * reach maximum if reach max value - lock the pup
+			 * if not - increment (Move to right one phase - ADLL)
+			 * dqs RX delay
+			 */
+			adll_idx++;
+			for (pup = 0; pup < max_pup; pup++) {
+				if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
+					/* Check only unlocked pups */
+					if ((adll_idx) >= ADLL_MAX) {
+						/* Reach maximum - lock the pup */
+						DEBUG_DQS_C("DDR3 - DQS - Special II - reach maximum - lock pup ",
+							    pup, 1);
+						unlock_pup &= ~(1 << pup);
+					} else {
+						/* Didn't reach maximum - increment ADLL */
+						ddr3_write_pup_reg(PUP_DQS_RD,
+								   cs,
+								   pup +
+								   (ecc *
+								    ECC_PUP), 0,
+								   (adll_idx));
+					}
+				}
+			}
+		} while (unlock_pup != 0);
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_set_dqs_centralization_results
+ * Desc:     Set to HW the DQS centralization phase results.
+ * Args:
+ *           is_tx             Indicates whether to set Tx or RX results
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_set_dqs_centralization_results(MV_DRAM_INFO *dram_info, u32 cs,
+					u32 ecc, int is_tx)
+{
+	u32 pup, pup_num;
+	int addl_val;
+	u32 max_pup;
+
+	max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups);
+
+	DEBUG_DQS_RESULTS_S("\n############ LOG LEVEL 2(Windows margins) ############\n");
+
+	if (is_tx) {
+		DEBUG_DQS_RESULTS_C("DDR3 - DQS TX - Set Dqs Centralization Results - CS: ",
+				    cs, 1);
+	} else {
+		DEBUG_DQS_RESULTS_C("DDR3 - DQS RX - Set Dqs Centralization Results - CS: ",
+				    cs, 1);
+	}
+
+	/* Set adll to center = (General_High_limit + General_Low_limit)/2 */
+	DEBUG_DQS_RESULTS_S("\nDQS    LOW     HIGH     WIN-SIZE      Set\n");
+	DEBUG_DQS_RESULTS_S("==============================================\n");
+	for (pup = 0; pup < max_pup; pup++) {
+		addl_val = (centralization_high_limit[pup] +
+			    centralization_low_limit[pup]) / 2;
+
+		pup_num = pup * (1 - ecc) + ecc * ECC_PUP;
+
+		DEBUG_DQS_RESULTS_D(pup_num, 1);
+		DEBUG_DQS_RESULTS_S("     0x");
+		DEBUG_DQS_RESULTS_D(centralization_low_limit[pup], 2);
+		DEBUG_DQS_RESULTS_S("      0x");
+		DEBUG_DQS_RESULTS_D(centralization_high_limit[pup], 2);
+		DEBUG_DQS_RESULTS_S("      0x");
+		DEBUG_DQS_RESULTS_D(centralization_high_limit[pup] -
+				    centralization_low_limit[pup], 2);
+		DEBUG_DQS_RESULTS_S("       0x");
+		DEBUG_DQS_RESULTS_D(addl_val, 2);
+		DEBUG_DQS_RESULTS_S("\n");
+
+		if (addl_val < ADLL_MIN) {
+			addl_val = ADLL_MIN;
+			DEBUG_DQS_RESULTS_S("DDR3 - DQS - Setting ADLL value for Pup to MIN (since it was lower than 0)\n");
+		}
+
+		if (addl_val > ADLL_MAX) {
+			addl_val = ADLL_MAX;
+			DEBUG_DQS_RESULTS_S("DDR3 - DQS - Setting ADLL value for Pup to MAX (since it was higher than 31)\n");
+		}
+
+		if (is_tx) {
+			ddr3_write_pup_reg(PUP_DQS_WR, cs, pup_num, 0,
+					   addl_val +
+					   dram_info->wl_val[cs][pup_num][D]);
+		} else {
+			ddr3_write_pup_reg(PUP_DQS_RD, cs, pup_num, 0,
+					   addl_val);
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Set training patterns
+ */
+int ddr3_load_dqs_patterns(MV_DRAM_INFO *dram_info)
+{
+	u32 cs, cs_count, cs_tmp, victim_dq;
+	u32 sdram_addr;
+	u32 *pattern_ptr;
+
+	/* Loop for each CS */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			cs_count = 0;
+			for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) {
+				if (dram_info->cs_ena & (1 << cs_tmp))
+					cs_count++;
+			}
+
+			/* Init killer pattern */
+			sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) +
+				      SDRAM_DQS_RX_OFFS);
+			for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+				pattern_ptr = ddr3_dqs_choose_pattern(dram_info,
+								      victim_dq);
+				if (MV_OK != ddr3_sdram_dqs_compare(
+					    dram_info, (u32)NULL, NULL,
+					    pattern_ptr, LEN_KILLER_PATTERN,
+					    sdram_addr + LEN_KILLER_PATTERN *
+					    4 * victim_dq, 1, 0, NULL,
+					    0))
+					return MV_DDR3_TRAINING_ERR_DQS_PATTERN;
+			}
+
+			/* Init special-killer pattern */
+			sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) +
+				      SDRAM_DQS_RX_SPECIAL_OFFS);
+			for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) {
+				if (MV_OK != ddr3_sdram_dqs_compare(
+					    dram_info, (u32)NULL, NULL,
+					    special_pattern[victim_dq],
+					    LEN_KILLER_PATTERN, sdram_addr +
+					    LEN_KILLER_PATTERN * 4 * victim_dq,
+					    1, 0, NULL, 0))
+					return MV_DDR3_TRAINING_ERR_DQS_PATTERN;
+			}
+		}
+	}
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/axp/ddr3_hw_training.c b/drivers/ddr/marvell/axp/ddr3_hw_training.c
new file mode 100644
index 0000000..cc3a77c
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_hw_training.c
@@ -0,0 +1,1114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_init.h"
+#include "ddr3_hw_training.h"
+#include "xor.h"
+
+#ifdef MV88F78X60
+#include "ddr3_patterns_64bit.h"
+#else
+#include "ddr3_patterns_16bit.h"
+#if defined(MV88F672X)
+#include "ddr3_patterns_16bit.h"
+#endif
+#endif
+
+/*
+ * Debug
+ */
+
+#define DEBUG_MAIN_C(s, d, l) \
+	DEBUG_MAIN_S(s); DEBUG_MAIN_D(d, l); DEBUG_MAIN_S("\n")
+#define DEBUG_MAIN_FULL_C(s, d, l) \
+	DEBUG_MAIN_FULL_S(s); DEBUG_MAIN_FULL_D(d, l); DEBUG_MAIN_FULL_S("\n")
+
+#ifdef MV_DEBUG_MAIN
+#define DEBUG_MAIN_S(s)			puts(s)
+#define DEBUG_MAIN_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_MAIN_S(s)
+#define DEBUG_MAIN_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_MAIN_FULL
+#define DEBUG_MAIN_FULL_S(s)		puts(s)
+#define DEBUG_MAIN_FULL_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_MAIN_FULL_S(s)
+#define DEBUG_MAIN_FULL_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_SUSPEND_RESUME
+#define DEBUG_SUSPEND_RESUME_S(s)	puts(s)
+#define DEBUG_SUSPEND_RESUME_D(d, l)	printf("%x", d)
+#else
+#define DEBUG_SUSPEND_RESUME_S(s)
+#define DEBUG_SUSPEND_RESUME_D(d, l)
+#endif
+
+static u32 ddr3_sw_wl_rl_debug;
+static u32 ddr3_run_pbs = 1;
+
+void ddr3_print_version(void)
+{
+	puts("DDR3 Training Sequence - Ver 5.7.");
+}
+
+void ddr3_set_sw_wl_rl_debug(u32 val)
+{
+	ddr3_sw_wl_rl_debug = val;
+}
+
+void ddr3_set_pbs(u32 val)
+{
+	ddr3_run_pbs = val;
+}
+
+int ddr3_hw_training(u32 target_freq, u32 ddr_width, int xor_bypass,
+		     u32 scrub_offs, u32 scrub_size, int dqs_clk_aligned,
+		     int debug_mode, int reg_dimm_skip_wl)
+{
+	/* A370 has no PBS mechanism */
+	__maybe_unused u32 first_loop_flag = 0;
+	u32 freq, reg;
+	MV_DRAM_INFO dram_info;
+	int ratio_2to1 = 0;
+	int tmp_ratio = 1;
+	int status;
+
+	if (debug_mode)
+		DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 1\n");
+
+	memset(&dram_info, 0, sizeof(dram_info));
+	dram_info.num_cs = ddr3_get_cs_num_from_reg();
+	dram_info.cs_ena = ddr3_get_cs_ena_from_reg();
+	dram_info.target_frequency = target_freq;
+	dram_info.ddr_width = ddr_width;
+	dram_info.num_of_std_pups = ddr_width / PUP_SIZE;
+	dram_info.rl400_bug = 0;
+	dram_info.multi_cs_mr_support = 0;
+#ifdef MV88F67XX
+	dram_info.rl400_bug = 1;
+#endif
+
+	/* Ignore ECC errors - if ECC is enabled */
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR);
+	if (reg & (1 << REG_SDRAM_CONFIG_ECC_OFFS)) {
+		dram_info.ecc_ena = 1;
+		reg |= (1 << REG_SDRAM_CONFIG_IERR_OFFS);
+		reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+	} else {
+		dram_info.ecc_ena = 0;
+	}
+
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR);
+	if (reg & (1 << REG_SDRAM_CONFIG_REGDIMM_OFFS))
+		dram_info.reg_dimm = 1;
+	else
+		dram_info.reg_dimm = 0;
+
+	dram_info.num_of_total_pups = ddr_width / PUP_SIZE + dram_info.ecc_ena;
+
+	/* Get target 2T value */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	dram_info.mode_2t = (reg >> REG_DUNIT_CTRL_LOW_2T_OFFS) &
+		REG_DUNIT_CTRL_LOW_2T_MASK;
+
+	/* Get target CL value */
+#ifdef MV88F67XX
+	reg = reg_read(REG_DDR3_MR0_ADDR) >> 2;
+#else
+	reg = reg_read(REG_DDR3_MR0_CS_ADDR) >> 2;
+#endif
+
+	reg = (((reg >> 1) & 0xE) | (reg & 0x1)) & 0xF;
+	dram_info.cl = ddr3_valid_cl_to_cl(reg);
+
+	/* Get target CWL value */
+#ifdef MV88F67XX
+	reg = reg_read(REG_DDR3_MR2_ADDR) >> REG_DDR3_MR2_CWL_OFFS;
+#else
+	reg = reg_read(REG_DDR3_MR2_CS_ADDR) >> REG_DDR3_MR2_CWL_OFFS;
+#endif
+
+	reg &= REG_DDR3_MR2_CWL_MASK;
+	dram_info.cwl = reg;
+#if !defined(MV88F67XX)
+	/* A370 has no PBS mechanism */
+#if defined(MV88F78X60)
+	if ((dram_info.target_frequency > DDR_400) && (ddr3_run_pbs))
+		first_loop_flag = 1;
+#else
+	/* first_loop_flag = 1; skip mid freq at ALP/A375 */
+	if ((dram_info.target_frequency > DDR_400) && (ddr3_run_pbs) &&
+	    (mv_ctrl_revision_get() >= UMC_A0))
+		first_loop_flag = 1;
+	else
+		first_loop_flag = 0;
+#endif
+#endif
+
+	freq = dram_info.target_frequency;
+
+	/* Set ODT to always on */
+	ddr3_odt_activate(1);
+
+	/* Init XOR */
+	mv_sys_xor_init(&dram_info);
+
+	/* Get DRAM/HCLK ratio */
+	if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS))
+		ratio_2to1 = 1;
+
+	/*
+	 * Xor Bypass - ECC support in AXP is currently available for 1:1
+	 * modes frequency modes.
+	 * Not all frequency modes support the ddr3 training sequence
+	 * (Only 1200/300).
+	 * Xor Bypass allows using the Xor initializations and scrubbing
+	 * inside the ddr3 training sequence without running the training
+	 * itself.
+	 */
+	if (xor_bypass == 0) {
+		if (ddr3_run_pbs) {
+			DEBUG_MAIN_S("DDR3 Training Sequence - Run with PBS.\n");
+		} else {
+			DEBUG_MAIN_S("DDR3 Training Sequence - Run without PBS.\n");
+		}
+
+		if (dram_info.target_frequency > DFS_MARGIN) {
+			tmp_ratio = 0;
+			freq = DDR_100;
+
+			if (dram_info.reg_dimm == 1)
+				freq = DDR_300;
+
+			if (MV_OK != ddr3_dfs_high_2_low(freq, &dram_info)) {
+				/* Set low - 100Mhz DDR Frequency by HW */
+				DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Dfs High2Low)\n");
+				return MV_DDR3_TRAINING_ERR_DFS_H2L;
+			}
+
+			if ((dram_info.reg_dimm == 1) &&
+			    (reg_dimm_skip_wl == 0)) {
+				if (MV_OK !=
+				    ddr3_write_leveling_hw_reg_dimm(freq,
+								    &dram_info))
+					DEBUG_MAIN_S("DDR3 Training Sequence - Registered DIMM Low WL - SKIP\n");
+			}
+
+			if (ddr3_get_log_level() >= MV_LOG_LEVEL_1)
+				ddr3_print_freq(freq);
+
+			if (debug_mode)
+				DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 2\n");
+		} else {
+			if (!dqs_clk_aligned) {
+#ifdef MV88F67XX
+				/*
+				 * If running training sequence without DFS,
+				 * we must run Write leveling before writing
+				 * the patterns
+				 */
+
+				/*
+				 * ODT - Multi CS system use SW WL,
+				 * Single CS System use HW WL
+				 */
+				if (dram_info.cs_ena > 1) {
+					if (MV_OK !=
+					    ddr3_write_leveling_sw(
+						    freq, tmp_ratio,
+						    &dram_info)) {
+						DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n");
+						return MV_DDR3_TRAINING_ERR_WR_LVL_SW;
+					}
+				} else {
+					if (MV_OK !=
+					    ddr3_write_leveling_hw(freq,
+								   &dram_info)) {
+						DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n");
+						return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+					}
+				}
+#else
+				if (MV_OK != ddr3_write_leveling_hw(
+					    freq, &dram_info)) {
+					DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n");
+					if (ddr3_sw_wl_rl_debug) {
+						if (MV_OK !=
+						    ddr3_write_leveling_sw(
+							    freq, tmp_ratio,
+							    &dram_info)) {
+							DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n");
+							return MV_DDR3_TRAINING_ERR_WR_LVL_SW;
+						}
+					} else {
+						return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+					}
+				}
+#endif
+			}
+
+			if (debug_mode)
+				DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 3\n");
+		}
+
+		if (MV_OK != ddr3_load_patterns(&dram_info, 0)) {
+			DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Loading Patterns)\n");
+			return MV_DDR3_TRAINING_ERR_LOAD_PATTERNS;
+		}
+
+		/*
+		 * TODO:
+		 * The mainline U-Boot port of the bin_hdr DDR training code
+		 * needs a delay of minimum 20ms here (10ms is a bit too short
+		 * and the CPU hangs). The bin_hdr code doesn't have this delay.
+		 * To be save here, lets add a delay of 50ms here.
+		 *
+		 * Tested on the Marvell DB-MV784MP-GP board
+		 */
+		mdelay(50);
+
+		do {
+			freq = dram_info.target_frequency;
+			tmp_ratio = ratio_2to1;
+			DEBUG_MAIN_FULL_S("DDR3 Training Sequence - DEBUG - 4\n");
+
+#if defined(MV88F78X60)
+			/*
+			 * There is a difference on the DFS frequency at the
+			 * first iteration of this loop
+			 */
+			if (first_loop_flag) {
+				freq = DDR_400;
+				tmp_ratio = 0;
+			}
+#endif
+
+			if (MV_OK != ddr3_dfs_low_2_high(freq, tmp_ratio,
+							 &dram_info)) {
+				DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Dfs Low2High)\n");
+				return MV_DDR3_TRAINING_ERR_DFS_H2L;
+			}
+
+			if (ddr3_get_log_level() >= MV_LOG_LEVEL_1) {
+				ddr3_print_freq(freq);
+			}
+
+			if (debug_mode)
+				DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 5\n");
+
+			/* Write leveling */
+			if (!dqs_clk_aligned) {
+#ifdef MV88F67XX
+				/*
+				 * ODT - Multi CS system that not support Multi
+				 * CS MRS commands must use SW WL
+				 */
+				if (dram_info.cs_ena > 1) {
+					if (MV_OK != ddr3_write_leveling_sw(
+						    freq, tmp_ratio, &dram_info)) {
+						DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n");
+						return MV_DDR3_TRAINING_ERR_WR_LVL_SW;
+					}
+				} else {
+					if (MV_OK != ddr3_write_leveling_hw(
+						    freq, &dram_info)) {
+						DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n");
+						return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+					}
+				}
+#else
+				if ((dram_info.reg_dimm == 1) &&
+				    (freq == DDR_400)) {
+					if (reg_dimm_skip_wl == 0) {
+						if (MV_OK != ddr3_write_leveling_hw_reg_dimm(
+							    freq, &dram_info))
+							DEBUG_MAIN_S("DDR3 Training Sequence - Registered DIMM WL - SKIP\n");
+					}
+				} else {
+					if (MV_OK != ddr3_write_leveling_hw(
+						    freq, &dram_info)) {
+						DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n");
+						if (ddr3_sw_wl_rl_debug) {
+							if (MV_OK != ddr3_write_leveling_sw(
+								    freq, tmp_ratio, &dram_info)) {
+								DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n");
+								return MV_DDR3_TRAINING_ERR_WR_LVL_SW;
+							}
+						} else {
+							return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+						}
+					}
+				}
+#endif
+				if (debug_mode)
+					DEBUG_MAIN_S
+					    ("DDR3 Training Sequence - DEBUG - 6\n");
+			}
+
+			/* Read Leveling */
+			/*
+			 * Armada 370 - Support for HCLK @ 400MHZ - must use
+			 * SW read leveling
+			 */
+			if (freq == DDR_400 && dram_info.rl400_bug) {
+				status = ddr3_read_leveling_sw(freq, tmp_ratio,
+						       &dram_info);
+				if (MV_OK != status) {
+					DEBUG_MAIN_S
+					    ("DDR3 Training Sequence - FAILED (Read Leveling Sw)\n");
+					return status;
+				}
+			} else {
+				if (MV_OK != ddr3_read_leveling_hw(
+					    freq, &dram_info)) {
+					DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Read Leveling Hw)\n");
+					if (ddr3_sw_wl_rl_debug) {
+						if (MV_OK != ddr3_read_leveling_sw(
+							    freq, tmp_ratio,
+							    &dram_info)) {
+							DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Read Leveling Sw)\n");
+							return MV_DDR3_TRAINING_ERR_WR_LVL_SW;
+						}
+					} else {
+						return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+					}
+				}
+			}
+
+			if (debug_mode)
+				DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 7\n");
+
+			if (MV_OK != ddr3_wl_supplement(&dram_info)) {
+				DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hi-Freq Sup)\n");
+				return MV_DDR3_TRAINING_ERR_WR_LVL_HI_FREQ;
+			}
+
+			if (debug_mode)
+				DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 8\n");
+#if !defined(MV88F67XX)
+			/* A370 has no PBS mechanism */
+#if defined(MV88F78X60) || defined(MV88F672X)
+			if (first_loop_flag == 1) {
+				first_loop_flag = 0;
+
+				status = MV_OK;
+				status = ddr3_pbs_rx(&dram_info);
+				if (MV_OK != status) {
+					DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (PBS RX)\n");
+					return status;
+				}
+
+				if (debug_mode)
+					DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 9\n");
+
+				status = ddr3_pbs_tx(&dram_info);
+				if (MV_OK != status) {
+					DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (PBS TX)\n");
+					return status;
+				}
+
+				if (debug_mode)
+					DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 10\n");
+			}
+#endif
+#endif
+		} while (freq != dram_info.target_frequency);
+
+		status = ddr3_dqs_centralization_rx(&dram_info);
+		if (MV_OK != status) {
+			DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (DQS Centralization RX)\n");
+			return status;
+		}
+
+		if (debug_mode)
+			DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 11\n");
+
+		status = ddr3_dqs_centralization_tx(&dram_info);
+		if (MV_OK != status) {
+			DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (DQS Centralization TX)\n");
+			return status;
+		}
+
+		if (debug_mode)
+			DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 12\n");
+	}
+
+	ddr3_set_performance_params(&dram_info);
+
+	if (dram_info.ecc_ena) {
+		/* Need to SCRUB the DRAM memory area to load U-Boot */
+		mv_sys_xor_finish();
+		dram_info.num_cs = 1;
+		dram_info.cs_ena = 1;
+		mv_sys_xor_init(&dram_info);
+		mv_xor_mem_init(0, scrub_offs, scrub_size, 0xdeadbeef,
+				0xdeadbeef);
+
+		/* Wait for previous transfer completion */
+		while (mv_xor_state_get(0) != MV_IDLE)
+			;
+
+		if (debug_mode)
+			DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 13\n");
+	}
+
+	/* Return XOR State */
+	mv_sys_xor_finish();
+
+#if defined(MV88F78X60)
+	/* Save training results in memeory for resume state */
+	ddr3_save_training(&dram_info);
+#endif
+	/* Clear ODT always on */
+	ddr3_odt_activate(0);
+
+	/* Configure Dynamic read ODT */
+	ddr3_odt_read_dynamic_config(&dram_info);
+
+	return MV_OK;
+}
+
+void ddr3_set_performance_params(MV_DRAM_INFO *dram_info)
+{
+	u32 twr2wr, trd2rd, trd2wr_wr2rd;
+	u32 tmp1, tmp2, reg;
+
+	DEBUG_MAIN_FULL_C("Max WL Phase: ", dram_info->wl_max_phase, 2);
+	DEBUG_MAIN_FULL_C("Min WL Phase: ", dram_info->wl_min_phase, 2);
+	DEBUG_MAIN_FULL_C("Max RL Phase: ", dram_info->rl_max_phase, 2);
+	DEBUG_MAIN_FULL_C("Min RL Phase: ", dram_info->rl_min_phase, 2);
+
+	if (dram_info->wl_max_phase < 2)
+		twr2wr = 0x2;
+	else
+		twr2wr = 0x3;
+
+	trd2rd = 0x1 + (dram_info->rl_max_phase + 1) / 2 +
+		(dram_info->rl_max_phase + 1) % 2;
+
+	tmp1 = (dram_info->rl_max_phase - dram_info->wl_min_phase) / 2 +
+		(((dram_info->rl_max_phase - dram_info->wl_min_phase) % 2) >
+		 0 ? 1 : 0);
+	tmp2 = (dram_info->wl_max_phase - dram_info->rl_min_phase) / 2 +
+		((dram_info->wl_max_phase - dram_info->rl_min_phase) % 2 >
+		 0 ? 1 : 0);
+	trd2wr_wr2rd = (tmp1 >= tmp2) ? tmp1 : tmp2;
+
+	trd2wr_wr2rd += 2;
+	trd2rd += 2;
+	twr2wr += 2;
+
+	DEBUG_MAIN_FULL_C("WR 2 WR: ", twr2wr, 2);
+	DEBUG_MAIN_FULL_C("RD 2 RD: ", trd2rd, 2);
+	DEBUG_MAIN_FULL_C("RD 2 WR / WR 2 RD: ", trd2wr_wr2rd, 2);
+
+	reg = reg_read(REG_SDRAM_TIMING_HIGH_ADDR);
+
+	reg &= ~(REG_SDRAM_TIMING_H_W2W_MASK << REG_SDRAM_TIMING_H_W2W_OFFS);
+	reg |= ((twr2wr & REG_SDRAM_TIMING_H_W2W_MASK) <<
+		REG_SDRAM_TIMING_H_W2W_OFFS);
+
+	reg &= ~(REG_SDRAM_TIMING_H_R2R_MASK << REG_SDRAM_TIMING_H_R2R_OFFS);
+	reg &= ~(REG_SDRAM_TIMING_H_R2R_H_MASK <<
+		 REG_SDRAM_TIMING_H_R2R_H_OFFS);
+	reg |= ((trd2rd & REG_SDRAM_TIMING_H_R2R_MASK) <<
+		REG_SDRAM_TIMING_H_R2R_OFFS);
+	reg |= (((trd2rd >> 2) & REG_SDRAM_TIMING_H_R2R_H_MASK) <<
+		REG_SDRAM_TIMING_H_R2R_H_OFFS);
+
+	reg &= ~(REG_SDRAM_TIMING_H_R2W_W2R_MASK <<
+		 REG_SDRAM_TIMING_H_R2W_W2R_OFFS);
+	reg &= ~(REG_SDRAM_TIMING_H_R2W_W2R_H_MASK <<
+		 REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS);
+	reg |= ((trd2wr_wr2rd & REG_SDRAM_TIMING_H_R2W_W2R_MASK) <<
+		REG_SDRAM_TIMING_H_R2W_W2R_OFFS);
+	reg |= (((trd2wr_wr2rd >> 2) & REG_SDRAM_TIMING_H_R2W_W2R_H_MASK) <<
+		REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS);
+
+	reg_write(REG_SDRAM_TIMING_HIGH_ADDR, reg);
+}
+
+/*
+ * Perform DDR3 PUP Indirect Write
+ */
+void ddr3_write_pup_reg(u32 mode, u32 cs, u32 pup, u32 phase, u32 delay)
+{
+	u32 reg = 0;
+
+	if (pup == PUP_BC)
+		reg |= (1 << REG_PHY_BC_OFFS);
+	else
+		reg |= (pup << REG_PHY_PUP_OFFS);
+
+	reg |= ((0x4 * cs + mode) << REG_PHY_CS_OFFS);
+	reg |= (phase << REG_PHY_PHASE_OFFS) | delay;
+
+	if (mode == PUP_WL_MODE)
+		reg |= ((INIT_WL_DELAY + delay) << REG_PHY_DQS_REF_DLY_OFFS);
+
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+	reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+
+	do {
+		reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
+			REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+	} while (reg);	/* Wait for '0' to mark the end of the transaction */
+
+	/* If read Leveling mode - need to write to register 3 separetly */
+	if (mode == PUP_RL_MODE) {
+		reg = 0;
+
+		if (pup == PUP_BC)
+			reg |= (1 << REG_PHY_BC_OFFS);
+		else
+			reg |= (pup << REG_PHY_PUP_OFFS);
+
+		reg |= ((0x4 * cs + mode + 1) << REG_PHY_CS_OFFS);
+		reg |= (INIT_RL_DELAY);
+
+		reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */
+		reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
+		reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */
+
+		do {
+			reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
+				REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+		} while (reg);
+	}
+}
+
+/*
+ * Perform DDR3 PUP Indirect Read
+ */
+u32 ddr3_read_pup_reg(u32 mode, u32 cs, u32 pup)
+{
+	u32 reg;
+
+	reg = (pup << REG_PHY_PUP_OFFS) |
+		((0x4 * cs + mode) << REG_PHY_CS_OFFS);
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+
+	reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_RD;
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+
+	do {
+		reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
+			REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+	} while (reg);	/* Wait for '0' to mark the end of the transaction */
+
+	return reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR);	/* 0x16A0 */
+}
+
+/*
+ * Set training patterns
+ */
+int ddr3_load_patterns(MV_DRAM_INFO *dram_info, int resume)
+{
+	u32 reg;
+
+	/* Enable SW override - Required for the ECC Pup */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	if (resume == 0) {
+#if defined(MV88F78X60) || defined(MV88F672X)
+		ddr3_load_pbs_patterns(dram_info);
+#endif
+		ddr3_load_dqs_patterns(dram_info);
+	}
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
+		(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
+	reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
+
+	/* Set Base Addr */
+#if defined(MV88F67XX)
+	reg_write(REG_DRAM_TRAINING_PATTERN_BASE_ADDR, 0);
+#else
+	if (resume == 0)
+		reg_write(REG_DRAM_TRAINING_PATTERN_BASE_ADDR, 0);
+	else
+		reg_write(REG_DRAM_TRAINING_PATTERN_BASE_ADDR,
+			  RESUME_RL_PATTERNS_ADDR);
+#endif
+
+	/* Set Patterns */
+	if (resume == 0) {
+		reg = (dram_info->cs_ena << REG_DRAM_TRAINING_CS_OFFS) |
+			(1 << REG_DRAM_TRAINING_PATTERNS_OFFS);
+	} else {
+		reg = (0x1 << REG_DRAM_TRAINING_CS_OFFS) |
+			(1 << REG_DRAM_TRAINING_PATTERNS_OFFS);
+	}
+
+	reg |= (1 << REG_DRAM_TRAINING_AUTO_OFFS);
+
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);
+
+	udelay(100);
+
+	/* Check if Successful */
+	if (reg_read(REG_DRAM_TRAINING_ADDR) &
+	    (1 << REG_DRAM_TRAINING_ERROR_OFFS))
+		return MV_OK;
+	else
+		return MV_FAIL;
+}
+
+#if !defined(MV88F67XX)
+/*
+ * Name:     ddr3_save_training(MV_DRAM_INFO *dram_info)
+ * Desc:     saves the training results to memeory (RL,WL,PBS,Rx/Tx
+ *           Centeralization)
+ * Args:     MV_DRAM_INFO *dram_info
+ * Notes:
+ * Returns:  None.
+ */
+void ddr3_save_training(MV_DRAM_INFO *dram_info)
+{
+	u32 val, pup, tmp_cs, cs, i, dq;
+	u32 crc = 0;
+	u32 regs = 0;
+	u32 *sdram_offset = (u32 *)RESUME_TRAINING_VALUES_ADDR;
+	u32 mode_config[MAX_TRAINING_MODE];
+
+	mode_config[DQS_WR_MODE] = PUP_DQS_WR;
+	mode_config[WL_MODE_] = PUP_WL_MODE;
+	mode_config[RL_MODE_] = PUP_RL_MODE;
+	mode_config[DQS_RD_MODE] = PUP_DQS_RD;
+	mode_config[PBS_TX_DM_MODE] = PUP_PBS_TX_DM;
+	mode_config[PBS_TX_MODE] = PUP_PBS_TX;
+	mode_config[PBS_RX_MODE] = PUP_PBS_RX;
+
+	/* num of training modes */
+	for (i = 0; i < MAX_TRAINING_MODE; i++) {
+		tmp_cs = dram_info->cs_ena;
+		/* num of CS */
+		for (cs = 0; cs < MAX_CS; cs++) {
+			if (tmp_cs & (1 << cs)) {
+				/* num of PUPs */
+				for (pup = 0; pup < dram_info->num_of_total_pups;
+				     pup++) {
+					if (pup == dram_info->num_of_std_pups &&
+					    dram_info->ecc_ena)
+						pup = ECC_PUP;
+					if (i == PBS_TX_DM_MODE) {
+						/*
+						 * Change CS bitmask because
+						 * PBS works only with CS0
+						 */
+						tmp_cs = 0x1;
+						val = ddr3_read_pup_reg(
+							mode_config[i], CS0, pup);
+					} else if (i == PBS_TX_MODE ||
+						   i == PBS_RX_MODE) {
+						/*
+						 * Change CS bitmask because
+						 * PBS works only with CS0
+						 */
+						tmp_cs = 0x1;
+						for (dq = 0; dq <= DQ_NUM;
+						     dq++) {
+							val = ddr3_read_pup_reg(
+								mode_config[i] + dq,
+								CS0,
+								pup);
+							(*sdram_offset) = val;
+							crc += *sdram_offset;
+							sdram_offset++;
+							regs++;
+						}
+						continue;
+					} else {
+						val = ddr3_read_pup_reg(
+							mode_config[i], cs, pup);
+					}
+
+					*sdram_offset = val;
+					crc += *sdram_offset;
+					sdram_offset++;
+					regs++;
+				}
+			}
+		}
+	}
+
+	*sdram_offset = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+	crc += *sdram_offset;
+	sdram_offset++;
+	regs++;
+	*sdram_offset = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+	crc += *sdram_offset;
+	sdram_offset++;
+	regs++;
+	sdram_offset = (u32 *)NUM_OF_REGISTER_ADDR;
+	*sdram_offset = regs;
+	DEBUG_SUSPEND_RESUME_S("Training Results CheckSum write= ");
+	DEBUG_SUSPEND_RESUME_D(crc, 8);
+	DEBUG_SUSPEND_RESUME_S("\n");
+	sdram_offset = (u32 *)CHECKSUM_RESULT_ADDR;
+	*sdram_offset = crc;
+}
+
+/*
+ * Name:     ddr3_read_training_results()
+ * Desc:     Reads the training results from memeory (RL,WL,PBS,Rx/Tx
+ *           Centeralization)
+ *           and writes them to the relevant registers
+ * Args:     MV_DRAM_INFO *dram_info
+ * Notes:
+ * Returns:  None.
+ */
+int ddr3_read_training_results(void)
+{
+	u32 val, reg, idx, dqs_wr_idx = 0, crc = 0;
+	u32 *sdram_offset = (u32 *)RESUME_TRAINING_VALUES_ADDR;
+	u32 training_val[RESUME_TRAINING_VALUES_MAX] = { 0 };
+	u32 regs = *((u32 *)NUM_OF_REGISTER_ADDR);
+
+	/*
+	 * Read Training results & Dunit registers from memory and write
+	 * it to an array
+	 */
+	for (idx = 0; idx < regs; idx++) {
+		training_val[idx] = *sdram_offset;
+		crc += *sdram_offset;
+		sdram_offset++;
+	}
+
+	sdram_offset = (u32 *)CHECKSUM_RESULT_ADDR;
+
+	if ((*sdram_offset) == crc) {
+		DEBUG_SUSPEND_RESUME_S("Training Results CheckSum read PASS= ");
+		DEBUG_SUSPEND_RESUME_D(crc, 8);
+		DEBUG_SUSPEND_RESUME_S("\n");
+	} else {
+		DEBUG_MAIN_S("Wrong Training Results CheckSum\n");
+		return MV_FAIL;
+	}
+
+	/*
+	 * We iterate through all the registers except for the last 2 since
+	 * they are Dunit registers (and not PHY registers)
+	 */
+	for (idx = 0; idx < (regs - 2); idx++) {
+		val = training_val[idx];
+		reg = (val >> REG_PHY_CS_OFFS) & 0x3F; /*read the phy address */
+
+		/* Check if the values belongs to the DQS WR */
+		if (reg == PUP_WL_MODE) {
+			/* bit[5:0] in DQS_WR are delay */
+			val = (training_val[dqs_wr_idx++] & 0x3F);
+			/*
+			 * bit[15:10] are DQS_WR delay & bit[9:0] are
+			 * WL phase & delay
+			 */
+			val = (val << REG_PHY_DQS_REF_DLY_OFFS) |
+				(training_val[idx] & 0x3C003FF);
+			/* Add Request pending and write operation bits */
+			val |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
+		} else if (reg == PUP_DQS_WR) {
+			/*
+			 * Do nothing since DQS_WR will be done in PUP_WL_MODE
+			 */
+			continue;
+		}
+
+		val |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
+		reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, val);
+		do {
+			val = (reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR)) &
+				REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+		} while (val);	/* Wait for '0' to mark the end of the transaction */
+	}
+
+	/* write last 2 Dunit configurations */
+	val = training_val[idx];
+	reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, val);	/* reg 0x1538 */
+	val = training_val[idx + 1];
+	reg_write(REG_READ_DATA_READY_DELAYS_ADDR, val);	/* reg 0x153c */
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_check_if_resume_mode()
+ * Desc:     Reads the address (0x3000) of the Resume Magic word (0xDEADB002)
+ * Args:     MV_DRAM_INFO *dram_info
+ * Notes:
+ * Returns:  return (magic_word == SUSPEND_MAGIC_WORD)
+ */
+int ddr3_check_if_resume_mode(MV_DRAM_INFO *dram_info, u32 freq)
+{
+	u32 magic_word;
+	u32 *sdram_offset = (u32 *)BOOT_INFO_ADDR;
+
+	if (dram_info->reg_dimm != 1) {
+		/*
+		 * Perform write levleling in order initiate the phy with
+		 * low frequency
+		 */
+		if (MV_OK != ddr3_write_leveling_hw(freq, dram_info)) {
+			DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n");
+			return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+		}
+	}
+
+	if (MV_OK != ddr3_load_patterns(dram_info, 1)) {
+		DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Loading Patterns)\n");
+		return MV_DDR3_TRAINING_ERR_LOAD_PATTERNS;
+	}
+
+	/* Enable CS0 only for RL */
+	dram_info->cs_ena = 0x1;
+
+	/* Perform Read levleling in order to get stable memory */
+	if (MV_OK != ddr3_read_leveling_hw(freq, dram_info)) {
+		DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Read Leveling Hw)\n");
+		return MV_DDR3_TRAINING_ERR_WR_LVL_HW;
+	}
+
+	/* Back to relevant CS */
+	dram_info->cs_ena = ddr3_get_cs_ena_from_reg();
+
+	magic_word = *sdram_offset;
+	return magic_word == SUSPEND_MAGIC_WORD;
+}
+
+/*
+ * Name:     ddr3_training_suspend_resume()
+ * Desc:     Execute the Resume state
+ * Args:     MV_DRAM_INFO *dram_info
+ * Notes:
+ * Returns:  return (magic_word == SUSPEND_MAGIC_WORD)
+ */
+int ddr3_training_suspend_resume(MV_DRAM_INFO *dram_info)
+{
+	u32 freq, reg;
+	int tmp_ratio;
+
+	/* Configure DDR */
+	if (MV_OK != ddr3_read_training_results())
+		return MV_FAIL;
+
+	/* Reset read FIFO */
+	reg = reg_read(REG_DRAM_TRAINING_ADDR);
+
+	/* Start Auto Read Leveling procedure */
+	reg |= (1 << REG_DRAM_TRAINING_RL_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS) +
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS));
+
+	/* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	udelay(2);
+
+	reg = reg_read(REG_DRAM_TRAINING_ADDR);
+	/* Clear Auto Read Leveling procedure */
+	reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	/* Return to target frequency */
+	freq = dram_info->target_frequency;
+	tmp_ratio = 1;
+	if (MV_OK != ddr3_dfs_low_2_high(freq, tmp_ratio, dram_info)) {
+		DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Dfs Low2High)\n");
+		return MV_DDR3_TRAINING_ERR_DFS_H2L;
+	}
+
+	if (dram_info->ecc_ena) {
+		/* Scabbling the RL area pattern and the training area */
+		mv_sys_xor_finish();
+		dram_info->num_cs = 1;
+		dram_info->cs_ena = 1;
+		mv_sys_xor_init(dram_info);
+		mv_xor_mem_init(0, RESUME_RL_PATTERNS_ADDR,
+				RESUME_RL_PATTERNS_SIZE, 0xFFFFFFFF, 0xFFFFFFFF);
+
+		/* Wait for previous transfer completion */
+
+		while (mv_xor_state_get(0) != MV_IDLE)
+			;
+
+		/* Return XOR State */
+		mv_sys_xor_finish();
+	}
+
+	return MV_OK;
+}
+#endif
+
+void ddr3_print_freq(u32 freq)
+{
+	u32 tmp_freq;
+
+	switch (freq) {
+	case 0:
+		tmp_freq = 100;
+		break;
+	case 1:
+		tmp_freq = 300;
+		break;
+	case 2:
+		tmp_freq = 360;
+		break;
+	case 3:
+		tmp_freq = 400;
+		break;
+	case 4:
+		tmp_freq = 444;
+		break;
+	case 5:
+		tmp_freq = 500;
+		break;
+	case 6:
+		tmp_freq = 533;
+		break;
+	case 7:
+		tmp_freq = 600;
+		break;
+	case 8:
+		tmp_freq = 666;
+		break;
+	case 9:
+		tmp_freq = 720;
+		break;
+	case 10:
+		tmp_freq = 800;
+		break;
+	default:
+		tmp_freq = 100;
+	}
+
+	printf("Current frequency is: %dMHz\n", tmp_freq);
+}
+
+int ddr3_get_min_max_read_sample_delay(u32 cs_enable, u32 reg, u32 *min,
+				       u32 *max, u32 *cs_max)
+{
+	u32 cs, delay;
+
+	*min = 0xFFFFFFFF;
+	*max = 0x0;
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if ((cs_enable & (1 << cs)) == 0)
+			continue;
+
+		delay = ((reg >> (cs * 8)) & 0x1F);
+
+		if (delay < *min)
+			*min = delay;
+
+		if (delay > *max) {
+			*max = delay;
+			*cs_max = cs;
+		}
+	}
+
+	return MV_OK;
+}
+
+int ddr3_get_min_max_rl_phase(MV_DRAM_INFO *dram_info, u32 *min, u32 *max,
+			      u32 cs)
+{
+	u32 pup, reg, phase;
+
+	*min = 0xFFFFFFFF;
+	*max = 0x0;
+
+	for (pup = 0; pup < dram_info->num_of_total_pups; pup++) {
+		reg = ddr3_read_pup_reg(PUP_RL_MODE, cs, pup);
+		phase = ((reg >> 8) & 0x7);
+
+		if (phase < *min)
+			*min = phase;
+
+		if (phase > *max)
+			*max = phase;
+	}
+
+	return MV_OK;
+}
+
+int ddr3_odt_activate(int activate)
+{
+	u32 reg, mask;
+
+	mask = (1 << REG_DUNIT_ODT_CTRL_OVRD_OFFS) |
+		(1 << REG_DUNIT_ODT_CTRL_OVRD_VAL_OFFS);
+	/* {0x0000149C}  -   DDR Dunit ODT Control Register */
+	reg = reg_read(REG_DUNIT_ODT_CTRL_ADDR);
+	if (activate)
+		reg |= mask;
+	else
+		reg &= ~mask;
+
+	reg_write(REG_DUNIT_ODT_CTRL_ADDR, reg);
+
+	return MV_OK;
+}
+
+int ddr3_odt_read_dynamic_config(MV_DRAM_INFO *dram_info)
+{
+	u32 min_read_sample_delay, max_read_sample_delay, max_rl_phase;
+	u32 min, max, cs_max;
+	u32 cs_ena, reg;
+
+	reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+	cs_ena = ddr3_get_cs_ena_from_reg();
+
+	/* Get minimum and maximum of read sample delay of all CS */
+	ddr3_get_min_max_read_sample_delay(cs_ena, reg, &min_read_sample_delay,
+					   &max_read_sample_delay, &cs_max);
+
+	/*
+	 * Get minimum and maximum read leveling phase which belongs to the
+	 * maximal read sample delay
+	 */
+	ddr3_get_min_max_rl_phase(dram_info, &min, &max, cs_max);
+	max_rl_phase = max;
+
+	/* DDR ODT Timing (Low) Register calculation */
+	reg = reg_read(REG_ODT_TIME_LOW_ADDR);
+	reg &= ~(0x1FF << REG_ODT_ON_CTL_RD_OFFS);
+	reg |= (((min_read_sample_delay - 1) & 0xF) << REG_ODT_ON_CTL_RD_OFFS);
+	reg |= (((max_read_sample_delay + 4 + (((max_rl_phase + 1) / 2) + 1)) &
+		 0x1F) << REG_ODT_OFF_CTL_RD_OFFS);
+	reg_write(REG_ODT_TIME_LOW_ADDR, reg);
+
+	return MV_OK;
+}
diff --git a/drivers/ddr/marvell/axp/ddr3_hw_training.h b/drivers/ddr/marvell/axp/ddr3_hw_training.h
new file mode 100644
index 0000000..30daaa9
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_hw_training.h
@@ -0,0 +1,391 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __DDR3_TRAINING_H
+#define __DDR3_TRAINING_H
+
+#include "ddr3_init.h"
+
+#ifdef MV88F78X60
+#include "ddr3_axp.h"
+#elif defined(MV88F67XX)
+#include "ddr3_a370.h"
+#elif defined(MV88F672X)
+#include "ddr3_a375.h"
+#endif
+
+/* The following is a list of Marvell status    */
+#define MV_ERROR	(-1)
+#define MV_OK		(0x00)	/* Operation succeeded                   */
+#define MV_FAIL		(0x01)	/* Operation failed                      */
+#define MV_BAD_VALUE	(0x02)	/* Illegal value (general)               */
+#define MV_OUT_OF_RANGE	(0x03)	/* The value is out of range             */
+#define MV_BAD_PARAM	(0x04)	/* Illegal parameter in function called  */
+#define MV_BAD_PTR	(0x05)	/* Illegal pointer value                 */
+#define MV_BAD_SIZE	(0x06)	/* Illegal size                          */
+#define MV_BAD_STATE	(0x07)	/* Illegal state of state machine        */
+#define MV_SET_ERROR	(0x08)	/* Set operation failed                  */
+#define MV_GET_ERROR	(0x09)	/* Get operation failed                  */
+#define MV_CREATE_ERROR	(0x0A)	/* Fail while creating an item           */
+#define MV_NOT_FOUND	(0x0B)	/* Item not found                        */
+#define MV_NO_MORE	(0x0C)	/* No more items found                   */
+#define MV_NO_SUCH	(0x0D)	/* No such item                          */
+#define MV_TIMEOUT	(0x0E)	/* Time Out                              */
+#define MV_NO_CHANGE	(0x0F)	/* Parameter(s) is already in this value */
+#define MV_NOT_SUPPORTED (0x10)	/* This request is not support           */
+#define MV_NOT_IMPLEMENTED (0x11) /* Request supported but not implemented*/
+#define MV_NOT_INITIALIZED (0x12) /* The item is not initialized          */
+#define MV_NO_RESOURCE	(0x13)	/* Resource not available (memory ...)   */
+#define MV_FULL		(0x14)	/* Item is full (Queue or table etc...)  */
+#define MV_EMPTY	(0x15)	/* Item is empty (Queue or table etc...) */
+#define MV_INIT_ERROR	(0x16)	/* Error occurred while INIT process      */
+#define MV_HW_ERROR	(0x17)	/* Hardware error                        */
+#define MV_TX_ERROR	(0x18)	/* Transmit operation not succeeded      */
+#define MV_RX_ERROR	(0x19)	/* Recieve operation not succeeded       */
+#define MV_NOT_READY	(0x1A)	/* The other side is not ready yet       */
+#define MV_ALREADY_EXIST (0x1B)	/* Tried to create existing item         */
+#define MV_OUT_OF_CPU_MEM   (0x1C) /* Cpu memory allocation failed.      */
+#define MV_NOT_STARTED	(0x1D)	/* Not started yet                       */
+#define MV_BUSY		(0x1E)	/* Item is busy.                         */
+#define MV_TERMINATE	(0x1F)	/* Item terminates it's work.            */
+#define MV_NOT_ALIGNED	(0x20)	/* Wrong alignment                       */
+#define MV_NOT_ALLOWED	(0x21)	/* Operation NOT allowed                 */
+#define MV_WRITE_PROTECT (0x22)	/* Write protected                       */
+
+#define MV_INVALID	(int)(-1)
+
+/*
+ * Debug (Enable/Disable modules) and Error report
+ */
+
+#ifdef BASIC_DEBUG
+#define MV_DEBUG_WL
+#define MV_DEBUG_RL
+#define MV_DEBUG_DQS_RESULTS
+#endif
+
+#ifdef FULL_DEBUG
+#define MV_DEBUG_WL
+#define MV_DEBUG_RL
+#define MV_DEBUG_DQS
+
+#define MV_DEBUG_PBS
+#define MV_DEBUG_DFS
+#define MV_DEBUG_MAIN_FULL
+#define MV_DEBUG_DFS_FULL
+#define MV_DEBUG_DQS_FULL
+#define MV_DEBUG_RL_FULL
+#define MV_DEBUG_WL_FULL
+#endif
+
+/*
+ * General Consts
+ */
+
+#define SDRAM_READ_WRITE_LEN_IN_WORDS           16
+#define SDRAM_READ_WRITE_LEN_IN_DOUBLE_WORDS    8
+#define CACHE_LINE_SIZE                         0x20
+
+#define SDRAM_CS_BASE                           0x0
+
+#define SRAM_BASE                               0x40000000
+#define SRAM_SIZE                               0xFFF
+
+#define LEN_64BIT_STD_PATTERN                   16
+#define LEN_64BIT_KILLER_PATTERN                128
+#define LEN_64BIT_SPECIAL_PATTERN               128
+#define LEN_64BIT_PBS_PATTERN                   16
+#define LEN_WL_SUP_PATTERN		                32
+
+#define LEN_16BIT_STD_PATTERN                   4
+#define LEN_16BIT_KILLER_PATTERN                128
+#define LEN_16BIT_SPECIAL_PATTERN               128
+#define LEN_16BIT_PBS_PATTERN                   4
+
+#define CMP_BYTE_SHIFT                          8
+#define CMP_BYTE_MASK                           0xFF
+#define PUP_SIZE                                8
+
+#define S 0
+#define C 1
+#define P 2
+#define D 3
+#define DQS 6
+#define PS 2
+#define DS 3
+#define PE 4
+#define DE 5
+
+#define CS0                                     0
+#define MAX_DIMM_NUM                            2
+#define MAX_DELAY                               0x1F
+
+/*
+ * Invertion limit and phase1 limit are WA for the RL @ 1:1 design bug -
+ * Armada 370 & AXP Z1
+ */
+#define MAX_DELAY_INV_LIMIT                     0x5
+#define MIN_DELAY_PHASE_1_LIMIT                 0x10
+
+#define MAX_DELAY_INV                           (0x3F - MAX_DELAY_INV_LIMIT)
+#define MIN_DELAY                               0
+#define MAX_PUP_NUM                             9
+#define ECC_PUP                                 8
+#define DQ_NUM                                  8
+#define DQS_DQ_NUM                              8
+#define INIT_WL_DELAY                           13
+#define INIT_RL_DELAY                           15
+#define TWLMRD_DELAY                            20
+#define TCLK_3_DELAY                            3
+#define ECC_BIT                                 8
+#define DMA_SIZE                                64
+#define MV_DMA_0                                0
+#define MAX_TRAINING_RETRY                      10
+
+#define PUP_RL_MODE                             0x2
+#define PUP_WL_MODE                             0
+#define PUP_PBS_TX                              0x10
+#define PUP_PBS_TX_DM                           0x1A
+#define PUP_PBS_RX                              0x30
+#define PUP_DQS_WR                              0x1
+#define PUP_DQS_RD                              0x3
+#define PUP_BC                                  10
+#define PUP_DELAY_MASK                          0x1F
+#define PUP_PHASE_MASK                          0x7
+#define PUP_NUM_64BIT                           8
+#define PUP_NUM_32BIT                           4
+#define PUP_NUM_16BIT                           2
+
+/* control PHY registers */
+#define CNTRL_PUP_DESKEW                        0x10
+
+/* WL */
+#define COUNT_WL_HI_FREQ                        2
+#define COUNT_WL                                2
+#define COUNT_WL_RFRS                           9
+#define WL_HI_FREQ_SHIFT                        2
+#define WL_HI_FREQ_STATE                        1
+#define COUNT_HW_WL                             2
+
+/* RL */
+/*
+ * RL_MODE - this define uses the RL mode SW RL instead of the functional
+ * window SW RL
+ */
+#define RL_MODE
+#define RL_WINDOW_WA
+#define MAX_PHASE_1TO1                          2
+#define MAX_PHASE_2TO1                          4
+
+#define MAX_PHASE_RL_UL_1TO1                    0
+#define MAX_PHASE_RL_L_1TO1                     4
+#define MAX_PHASE_RL_UL_2TO1                    3
+#define MAX_PHASE_RL_L_2TO1                     7
+
+#define RL_UNLOCK_STATE                         0
+#define RL_WINDOW_STATE                         1
+#define RL_FINAL_STATE                          2
+#define RL_RETRY_COUNT                          2
+#define COUNT_HW_RL                             2
+
+/* PBS */
+#define MAX_PBS                                 31
+#define MIN_PBS                                 0
+#define COUNT_PBS_PATTERN                       2
+#define COUNT_PBS_STARTOVER                     2
+#define COUNT_PBS_REPEAT                        3
+#define COUNT_PBS_COMP_RETRY_NUM                2
+#define PBS_DIFF_LIMIT                          31
+#define PATTERN_PBS_TX_A                        0x55555555
+#define PATTERN_PBS_TX_B                        0xAAAAAAAA
+
+/* DQS */
+#define ADLL_ERROR                              0x55
+#define ADLL_MAX                                31
+#define ADLL_MIN                                0
+#define MIN_WIN_SIZE                            4
+#define VALID_WIN_THRS                          MIN_WIN_SIZE
+
+#define MODE_2TO1                               1
+#define MODE_1TO1                               0
+
+/*
+ * Macros
+ */
+#define IS_PUP_ACTIVE(_data_, _pup_)        (((_data_) >> (_pup_)) & 0x1)
+
+/*
+ * Internal ERROR codes
+ */
+#define MV_DDR3_TRAINING_ERR_WR_LVL_HW              0xDD302001
+#define MV_DDR3_TRAINING_ERR_LOAD_PATTERNS          0xDD302002
+#define MV_DDR3_TRAINING_ERR_WR_LVL_HI_FREQ         0xDD302003
+#define MV_DDR3_TRAINING_ERR_DFS_H2L                0xDD302004
+#define MV_DDR3_TRAINING_ERR_DRAM_COMPARE           0xDD302005
+#define MV_DDR3_TRAINING_ERR_WIN_LIMITS             0xDD302006
+#define MV_DDR3_TRAINING_ERR_PUP_RANGE              0xDD302025
+#define MV_DDR3_TRAINING_ERR_DQS_LOW_LIMIT_SEARCH   0xDD302007
+#define MV_DDR3_TRAINING_ERR_DQS_HIGH_LIMIT_SEARCH  0xDD302008
+#define MV_DDR3_TRAINING_ERR_DQS_PATTERN            0xDD302009
+#define MV_DDR3_TRAINING_ERR_PBS_ADLL_SHR_1PHASE    0xDD302010
+#define MV_DDR3_TRAINING_ERR_PBS_TX_MAX_VAL         0xDD302011
+#define MV_DDR3_TRAINING_ERR_PBS_RX_PER_BIT         0xDD302012
+#define MV_DDR3_TRAINING_ERR_PBS_TX_PER_BIT         0xDD302013
+#define MV_DDR3_TRAINING_ERR_PBS_RX_MAX_VAL         0xDD302014
+#define MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP 0xDD302015
+#define MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_MAX_VAL  0xDD302016
+#define MV_DDR3_TRAINING_ERR_RD_LVL_RL_PATTERN      0xDD302017
+#define MV_DDR3_TRAINING_ERR_RD_LVL_RL_PUP_UNLOCK   0xDD302018
+#define MV_DDR3_TRAINING_ERR_RD_LVL_PUP_UNLOCK      0xDD302019
+#define MV_DDR3_TRAINING_ERR_WR_LVL_SW              0xDD302020
+#define MV_DDR3_TRAINING_ERR_PRBS_RX                0xDD302021
+#define MV_DDR3_TRAINING_ERR_DQS_RX                 0xDD302022
+#define MV_DDR3_TRAINING_ERR_PRBS_TX                0xDD302023
+#define MV_DDR3_TRAINING_ERR_DQS_TX                 0xDD302024
+
+/*
+ * DRAM information structure
+ */
+typedef struct dram_info {
+	u32 num_cs;
+	u32 cs_ena;
+	u32 num_of_std_pups;	/* Q value = ddrWidth/8 - Without ECC!! */
+	u32 num_of_total_pups;	/* numOfStdPups + eccEna */
+	u32 target_frequency;	/* DDR Frequency */
+	u32 ddr_width;		/* 32/64 Bit or 16/32 Bit */
+	u32 ecc_ena;		/* 0/1 */
+	u32 wl_val[MAX_CS][MAX_PUP_NUM][7];
+	u32 rl_val[MAX_CS][MAX_PUP_NUM][7];
+	u32 rl_max_phase;
+	u32 rl_min_phase;
+	u32 wl_max_phase;
+	u32 wl_min_phase;
+	u32 rd_smpl_dly;
+	u32 rd_rdy_dly;
+	u32 cl;
+	u32 cwl;
+	u32 mode_2t;
+	int rl400_bug;
+	int multi_cs_mr_support;
+	int reg_dimm;
+} MV_DRAM_INFO;
+
+enum training_modes  {
+	DQS_WR_MODE,
+	WL_MODE_,
+	RL_MODE_,
+	DQS_RD_MODE,
+	PBS_TX_DM_MODE,
+	PBS_TX_MODE,
+	PBS_RX_MODE,
+	MAX_TRAINING_MODE,
+};
+
+typedef struct dram_training_init {
+	u32 reg_addr;
+	u32 reg_value;
+} MV_DRAM_TRAINING_INIT;
+
+typedef struct dram_mv_init {
+	u32 reg_addr;
+	u32 reg_value;
+} MV_DRAM_MC_INIT;
+
+/* Board/Soc revisions define */
+enum board_rev {
+	Z1,
+	Z1_PCAC,
+	Z1_RD_SLED,
+	A0,
+	A0_AMC
+};
+
+typedef struct dram_modes {
+	char *mode_name;
+	u8 cpu_freq;
+	u8 fab_freq;
+	u8 chip_id;
+	int chip_board_rev;
+	MV_DRAM_MC_INIT *regs;
+	MV_DRAM_TRAINING_INIT *vals;
+} MV_DRAM_MODES;
+
+/*
+ * Function Declarations
+ */
+
+u32 cache_inv(u32 addr);
+void flush_l1_v7(u32 line);
+void flush_l1_v6(u32 line);
+
+u32 ddr3_cl_to_valid_cl(u32 cl);
+u32 ddr3_valid_cl_to_cl(u32 ui_valid_cl);
+
+void ddr3_write_pup_reg(u32 mode, u32 cs, u32 pup, u32 phase, u32 delay);
+u32 ddr3_read_pup_reg(u32 mode, u32 cs, u32 pup);
+
+int ddr3_sdram_pbs_compare(MV_DRAM_INFO *dram_info, u32 pup_locked, int is_tx,
+			   u32 pbs_pattern_idx, u32 pbs_curr_val,
+			   u32 pbs_lock_val, u32 *skew_array,
+			   u8 *unlock_pup_dq_array, u32 ecc);
+
+int ddr3_sdram_dqs_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+			   u32 *new_locked_pup, u32 *pattern,
+			   u32 pattern_len, u32 sdram_offset, int write,
+			   int mask, u32 *mask_pattern, int b_special_compare);
+
+int ddr3_sdram_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+		       u32 *new_locked_pup, u32 *pattern, u32 pattern_len,
+		       u32 sdram_offset, int write, int mask,
+		       u32 *mask_pattern, int b_special_compare);
+
+int ddr3_sdram_direct_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+			      u32 *new_locked_pup, u32 *pattern,
+			      u32 pattern_len, u32 sdram_offset, int write,
+			      int mask, u32 *mask_pattern);
+
+int ddr3_sdram_dm_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+			  u32 *new_locked_pup, u32 *pattern,
+			  u32 sdram_offset);
+int ddr3_dram_sram_read(u32 src, u32 dst, u32 len);
+int ddr3_load_patterns(MV_DRAM_INFO *dram_info, int resume);
+
+int ddr3_read_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info);
+int ddr3_read_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info);
+
+int ddr3_write_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info);
+int ddr3_write_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info);
+int ddr3_write_leveling_hw_reg_dimm(u32 freq, MV_DRAM_INFO *dram_info);
+int ddr3_wl_supplement(MV_DRAM_INFO *dram_info);
+
+int ddr3_dfs_high_2_low(u32 freq, MV_DRAM_INFO *dram_info);
+int ddr3_dfs_low_2_high(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info);
+
+int ddr3_pbs_tx(MV_DRAM_INFO *dram_info);
+int ddr3_pbs_rx(MV_DRAM_INFO *dram_info);
+int ddr3_load_pbs_patterns(MV_DRAM_INFO *dram_info);
+
+int ddr3_dqs_centralization_rx(MV_DRAM_INFO *dram_info);
+int ddr3_dqs_centralization_tx(MV_DRAM_INFO *dram_info);
+int ddr3_load_dqs_patterns(MV_DRAM_INFO *dram_info);
+
+void ddr3_static_training_init(void);
+
+u8 ddr3_get_eprom_fabric(void);
+void ddr3_set_performance_params(MV_DRAM_INFO *dram_info);
+int ddr3_dram_sram_burst(u32 src, u32 dst, u32 len);
+void ddr3_save_training(MV_DRAM_INFO *dram_info);
+int ddr3_read_training_results(void);
+int ddr3_training_suspend_resume(MV_DRAM_INFO *dram_info);
+int ddr3_get_min_max_read_sample_delay(u32 cs_enable, u32 reg, u32 *min,
+				       u32 *max, u32 *cs_max);
+int ddr3_get_min_max_rl_phase(MV_DRAM_INFO *dram_info, u32 *min, u32 *max,
+			      u32 cs);
+int ddr3_odt_activate(int activate);
+int ddr3_odt_read_dynamic_config(MV_DRAM_INFO *dram_info);
+void ddr3_print_freq(u32 freq);
+void ddr3_reset_phy_read_fifo(void);
+
+#endif /* __DDR3_TRAINING_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_init.c b/drivers/ddr/marvell/axp/ddr3_init.c
new file mode 100644
index 0000000..13df912
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_init.c
@@ -0,0 +1,1218 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_init.h"
+
+#if defined(MV88F78X60)
+#include "ddr3_axp_vars.h"
+#elif defined(MV88F67XX)
+#include "ddr3_a370_vars.h"
+#elif defined(MV88F672X)
+#include "ddr3_a375_vars.h"
+#endif
+
+#ifdef STATIC_TRAINING
+static void ddr3_static_training_init(void);
+#endif
+#ifdef DUNIT_STATIC
+static void ddr3_static_mc_init(void);
+#endif
+#if defined(DUNIT_STATIC) || defined(STATIC_TRAINING)
+MV_DRAM_MODES *ddr3_get_static_ddr_mode(void);
+#endif
+#if defined(MV88F672X)
+void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps);
+#endif
+u32 mv_board_id_get(void);
+extern void ddr3_set_sw_wl_rl_debug(u32);
+extern void ddr3_set_pbs(u32);
+extern void ddr3_set_log_level(u32 val);
+
+static u32 log_level = DDR3_LOG_LEVEL;
+
+static u32 ddr3_init_main(void);
+
+/*
+ * Name:     ddr3_set_log_level
+ * Desc:     This routine initialize the log_level acording to nLogLevel
+ *           which getting from user
+ * Args:     nLogLevel
+ * Notes:
+ * Returns:  None.
+ */
+void ddr3_set_log_level(u32 val)
+{
+	log_level = val;
+}
+
+/*
+ * Name:     ddr3_get_log_level
+ * Desc:     This routine returns the log level
+ * Args:     none
+ * Notes:
+ * Returns:  log level.
+ */
+u32 ddr3_get_log_level(void)
+{
+	return log_level;
+}
+
+static void debug_print_reg(u32 reg)
+{
+	printf("0x%08x = 0x%08x\n", reg, reg_read(reg));
+}
+
+static void print_dunit_setup(void)
+{
+	puts("\n########### LOG LEVEL 1 (D-UNIT SETUP)###########\n");
+
+#ifdef DUNIT_STATIC
+	puts("\nStatic D-UNIT Setup:\n");
+#endif
+#ifdef DUNIT_SPD
+	puts("\nDynamic(using SPD) D-UNIT Setup:\n");
+#endif
+	debug_print_reg(REG_SDRAM_CONFIG_ADDR);
+	debug_print_reg(REG_DUNIT_CTRL_LOW_ADDR);
+	debug_print_reg(REG_SDRAM_TIMING_LOW_ADDR);
+	debug_print_reg(REG_SDRAM_TIMING_HIGH_ADDR);
+	debug_print_reg(REG_SDRAM_ADDRESS_CTRL_ADDR);
+	debug_print_reg(REG_SDRAM_OPEN_PAGES_ADDR);
+	debug_print_reg(REG_SDRAM_OPERATION_ADDR);
+	debug_print_reg(REG_SDRAM_MODE_ADDR);
+	debug_print_reg(REG_SDRAM_EXT_MODE_ADDR);
+	debug_print_reg(REG_DDR_CONT_HIGH_ADDR);
+	debug_print_reg(REG_ODT_TIME_LOW_ADDR);
+	debug_print_reg(REG_SDRAM_ERROR_ADDR);
+	debug_print_reg(REG_SDRAM_AUTO_PWR_SAVE_ADDR);
+	debug_print_reg(REG_OUDDR3_TIMING_ADDR);
+	debug_print_reg(REG_ODT_TIME_HIGH_ADDR);
+	debug_print_reg(REG_SDRAM_ODT_CTRL_LOW_ADDR);
+	debug_print_reg(REG_SDRAM_ODT_CTRL_HIGH_ADDR);
+	debug_print_reg(REG_DUNIT_ODT_CTRL_ADDR);
+#ifndef MV88F67XX
+	debug_print_reg(REG_DRAM_FIFO_CTRL_ADDR);
+	debug_print_reg(REG_DRAM_AXI_CTRL_ADDR);
+	debug_print_reg(REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR);
+	debug_print_reg(REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR);
+	debug_print_reg(REG_DRAM_VER_CAL_MACHINE_CTRL_ADDR);
+	debug_print_reg(REG_DRAM_MAIN_PADS_CAL_ADDR);
+	debug_print_reg(REG_DRAM_HOR_CAL_MACHINE_CTRL_ADDR);
+	debug_print_reg(REG_CS_SIZE_SCRATCH_ADDR);
+	debug_print_reg(REG_DYNAMIC_POWER_SAVE_ADDR);
+	debug_print_reg(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+	debug_print_reg(REG_READ_DATA_READY_DELAYS_ADDR);
+	debug_print_reg(REG_DDR3_MR0_ADDR);
+	debug_print_reg(REG_DDR3_MR1_ADDR);
+	debug_print_reg(REG_DDR3_MR2_ADDR);
+	debug_print_reg(REG_DDR3_MR3_ADDR);
+	debug_print_reg(REG_DDR3_RANK_CTRL_ADDR);
+	debug_print_reg(REG_DRAM_PHY_CONFIG_ADDR);
+	debug_print_reg(REG_STATIC_DRAM_DLB_CONTROL);
+	debug_print_reg(DLB_BUS_OPTIMIZATION_WEIGHTS_REG);
+	debug_print_reg(DLB_AGING_REGISTER);
+	debug_print_reg(DLB_EVICTION_CONTROL_REG);
+	debug_print_reg(DLB_EVICTION_TIMERS_REGISTER_REG);
+#if defined(MV88F672X)
+	debug_print_reg(REG_FASTPATH_WIN_CTRL_ADDR(0));
+	debug_print_reg(REG_FASTPATH_WIN_BASE_ADDR(0));
+	debug_print_reg(REG_FASTPATH_WIN_CTRL_ADDR(1));
+	debug_print_reg(REG_FASTPATH_WIN_BASE_ADDR(1));
+#else
+	debug_print_reg(REG_FASTPATH_WIN_0_CTRL_ADDR);
+#endif
+	debug_print_reg(REG_CDI_CONFIG_ADDR);
+#endif
+}
+
+#if !defined(STATIC_TRAINING)
+static void ddr3_restore_and_set_final_windows(u32 *win_backup)
+{
+	u32 ui, reg, cs;
+	u32 win_ctrl_reg, num_of_win_regs;
+	u32 cs_ena = ddr3_get_cs_ena_from_reg();
+
+#if defined(MV88F672X)
+	if (DDR3_FAST_PATH_EN == 0)
+		return;
+#endif
+
+#if defined(MV88F672X)
+	win_ctrl_reg = REG_XBAR_WIN_16_CTRL_ADDR;
+	num_of_win_regs = 8;
+#else
+	win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR;
+	num_of_win_regs = 16;
+#endif
+
+	/* Return XBAR windows 4-7 or 16-19 init configuration */
+	for (ui = 0; ui < num_of_win_regs; ui++)
+		reg_write((win_ctrl_reg + 0x4 * ui), win_backup[ui]);
+
+	DEBUG_INIT_FULL_S("DDR3 Training Sequence - Switching XBAR Window to FastPath Window\n");
+
+#if defined(MV88F672X)
+	/* Set L2 filtering to 1G */
+	reg_write(0x8c04, 0x40000000);
+
+	/* Open fast path windows */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			/* set fast path window control for the cs */
+			reg = 0x1FFFFFE1;
+			reg |= (cs << 2);
+			reg |= (SDRAM_CS_SIZE & 0xFFFF0000);
+			/* Open fast path Window */
+			reg_write(REG_FASTPATH_WIN_CTRL_ADDR(cs), reg);
+			/* set fast path window base address for the cs */
+			reg = (((SDRAM_CS_SIZE + 1) * cs) & 0xFFFF0000);
+			/* Set base address */
+			reg_write(REG_FASTPATH_WIN_BASE_ADDR(cs), reg);
+		}
+	}
+#else
+	reg = 0x1FFFFFE1;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg |= (cs << 2);
+			break;
+		}
+	}
+
+	/* Open fast path Window to - 0.5G */
+	reg_write(REG_FASTPATH_WIN_0_CTRL_ADDR, reg);
+#endif
+}
+
+static void ddr3_save_and_set_training_windows(u32 *win_backup)
+{
+	u32 cs_ena = ddr3_get_cs_ena_from_reg();
+	u32 reg, tmp_count, cs, ui;
+	u32 win_ctrl_reg, win_base_reg, win_remap_reg;
+	u32 num_of_win_regs, win_jump_index;
+
+#if defined(MV88F672X)
+	/* Disable L2 filtering */
+	reg_write(0x8c04, 0);
+
+	win_ctrl_reg = REG_XBAR_WIN_16_CTRL_ADDR;
+	win_base_reg = REG_XBAR_WIN_16_BASE_ADDR;
+	win_remap_reg = REG_XBAR_WIN_16_REMAP_ADDR;
+	win_jump_index = 0x8;
+	num_of_win_regs = 8;
+#else
+	win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR;
+	win_base_reg = REG_XBAR_WIN_4_BASE_ADDR;
+	win_remap_reg = REG_XBAR_WIN_4_REMAP_ADDR;
+	win_jump_index = 0x10;
+	num_of_win_regs = 16;
+#endif
+
+	/* Close XBAR Window 19 - Not needed */
+	/* {0x000200e8}  -   Open Mbus Window - 2G */
+	reg_write(REG_XBAR_WIN_19_CTRL_ADDR, 0);
+
+	/* Save XBAR Windows 4-19 init configurations */
+	for (ui = 0; ui < num_of_win_regs; ui++)
+		win_backup[ui] = reg_read(win_ctrl_reg + 0x4 * ui);
+
+	/* Open XBAR Windows 4-7 or 16-19 for other CS */
+	reg = 0;
+	tmp_count = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			switch (cs) {
+			case 0:
+				reg = 0x0E00;
+				break;
+			case 1:
+				reg = 0x0D00;
+				break;
+			case 2:
+				reg = 0x0B00;
+				break;
+			case 3:
+				reg = 0x0700;
+				break;
+			}
+			reg |= (1 << 0);
+			reg |= (SDRAM_CS_SIZE & 0xFFFF0000);
+
+			reg_write(win_ctrl_reg + win_jump_index * tmp_count,
+				  reg);
+			reg = ((SDRAM_CS_SIZE + 1) * (tmp_count)) & 0xFFFF0000;
+			reg_write(win_base_reg + win_jump_index * tmp_count,
+				  reg);
+
+			if (win_remap_reg <= REG_XBAR_WIN_7_REMAP_ADDR) {
+				reg_write(win_remap_reg +
+					  win_jump_index * tmp_count, 0);
+			}
+
+			tmp_count++;
+		}
+	}
+}
+#endif /*  !defined(STATIC_TRAINING) */
+
+/*
+ * Name:     ddr3_init - Main DDR3 Init function
+ * Desc:     This routine initialize the DDR3 MC and runs HW training.
+ * Args:     None.
+ * Notes:
+ * Returns:  None.
+ */
+int ddr3_init(void)
+{
+	unsigned int status;
+
+	ddr3_set_pbs(DDR3_PBS);
+	ddr3_set_sw_wl_rl_debug(DDR3_RUN_SW_WHEN_HW_FAIL);
+
+	status = ddr3_init_main();
+	if (status == MV_DDR3_TRAINING_ERR_BAD_SAR)
+		DEBUG_INIT_S("DDR3 Training Error: Bad sample at reset");
+	if (status == MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP)
+		DEBUG_INIT_S("DDR3 Training Error: Bad DIMM setup");
+	if (status == MV_DDR3_TRAINING_ERR_MAX_CS_LIMIT)
+		DEBUG_INIT_S("DDR3 Training Error: Max CS limit");
+	if (status == MV_DDR3_TRAINING_ERR_MAX_ENA_CS_LIMIT)
+		DEBUG_INIT_S("DDR3 Training Error: Max enable CS limit");
+	if (status == MV_DDR3_TRAINING_ERR_BAD_R_DIMM_SETUP)
+		DEBUG_INIT_S("DDR3 Training Error: Bad R-DIMM setup");
+	if (status == MV_DDR3_TRAINING_ERR_TWSI_FAIL)
+		DEBUG_INIT_S("DDR3 Training Error: TWSI failure");
+	if (status == MV_DDR3_TRAINING_ERR_DIMM_TYPE_NO_MATCH)
+		DEBUG_INIT_S("DDR3 Training Error: DIMM type no match");
+	if (status == MV_DDR3_TRAINING_ERR_TWSI_BAD_TYPE)
+		DEBUG_INIT_S("DDR3 Training Error: TWSI bad type");
+	if (status == MV_DDR3_TRAINING_ERR_BUS_WIDTH_NOT_MATCH)
+		DEBUG_INIT_S("DDR3 Training Error: bus width no match");
+	if (status > MV_DDR3_TRAINING_ERR_HW_FAIL_BASE)
+		DEBUG_INIT_C("DDR3 Training Error: HW Failure 0x", status, 8);
+
+	return status;
+}
+
+static void print_ddr_target_freq(u32 cpu_freq, u32 fab_opt)
+{
+	puts("\nDDR3 Training Sequence - Run DDR3 at ");
+
+	switch (cpu_freq) {
+#if defined(MV88F672X)
+	case 21:
+		puts("533 Mhz\n");
+		break;
+#else
+	case 1:
+		puts("533 Mhz\n");
+		break;
+	case 2:
+		if (fab_opt == 5)
+			puts("600 Mhz\n");
+		if (fab_opt == 9)
+			puts("400 Mhz\n");
+		break;
+	case 3:
+		puts("667 Mhz\n");
+		break;
+	case 4:
+		if (fab_opt == 5)
+			puts("750 Mhz\n");
+		if (fab_opt == 9)
+			puts("500 Mhz\n");
+		break;
+	case 0xa:
+		puts("400 Mhz\n");
+		break;
+	case 0xb:
+		if (fab_opt == 5)
+			puts("800 Mhz\n");
+		if (fab_opt == 9)
+			puts("553 Mhz\n");
+		if (fab_opt == 0xA)
+			puts("640 Mhz\n");
+		break;
+#endif
+	default:
+		puts("NOT DEFINED FREQ\n");
+	}
+}
+
+static u32 ddr3_init_main(void)
+{
+	u32 target_freq;
+	u32 reg = 0;
+	u32 cpu_freq, fab_opt, hclk_time_ps, soc_num;
+	__maybe_unused u32 ecc = DRAM_ECC;
+	__maybe_unused int dqs_clk_aligned = 0;
+	__maybe_unused u32 scrub_offs, scrub_size;
+	__maybe_unused u32 ddr_width = BUS_WIDTH;
+	__maybe_unused int status;
+	__maybe_unused u32 win_backup[16];
+
+	/* SoC/Board special Initializtions */
+	fab_opt = ddr3_get_fab_opt();
+
+#ifdef CONFIG_SPD_EEPROM
+	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+#endif
+
+	ddr3_print_version();
+	DEBUG_INIT_S("4\n");
+	/* Lib version 5.5.4 */
+
+	fab_opt = ddr3_get_fab_opt();
+
+	/* Switching CPU to MRVL ID */
+	soc_num = (reg_read(REG_SAMPLE_RESET_HIGH_ADDR) & SAR1_CPU_CORE_MASK) >>
+		SAR1_CPU_CORE_OFFSET;
+	switch (soc_num) {
+	case 0x3:
+		reg_bit_set(CPU_CONFIGURATION_REG(3), CPU_MRVL_ID_OFFSET);
+		reg_bit_set(CPU_CONFIGURATION_REG(2), CPU_MRVL_ID_OFFSET);
+	case 0x1:
+		reg_bit_set(CPU_CONFIGURATION_REG(1), CPU_MRVL_ID_OFFSET);
+	case 0x0:
+		reg_bit_set(CPU_CONFIGURATION_REG(0), CPU_MRVL_ID_OFFSET);
+	default:
+		break;
+	}
+
+	/* Power down deskew PLL */
+#if !defined(MV88F672X)
+	/* 0x18780 [25] */
+	reg = (reg_read(REG_DDRPHY_APLL_CTRL_ADDR) & ~(1 << 25));
+	reg_write(REG_DDRPHY_APLL_CTRL_ADDR, reg);
+#endif
+
+	/*
+	 * Stage 0 - Set board configuration
+	 */
+	cpu_freq = ddr3_get_cpu_freq();
+	if (fab_opt > FAB_OPT)
+		fab_opt = FAB_OPT - 1;
+
+	if (ddr3_get_log_level() > 0)
+		print_ddr_target_freq(cpu_freq, fab_opt);
+
+#if defined(MV88F672X)
+	get_target_freq(cpu_freq, &target_freq, &hclk_time_ps);
+#else
+	target_freq = cpu_ddr_ratios[fab_opt][cpu_freq];
+	hclk_time_ps = cpu_fab_clk_to_hclk[fab_opt][cpu_freq];
+#endif
+	if ((target_freq == 0) || (hclk_time_ps == 0)) {
+		DEBUG_INIT_S("DDR3 Training Sequence - FAILED - Wrong Sample at Reset Configurations\n");
+		if (target_freq == 0) {
+			DEBUG_INIT_C("target_freq", target_freq, 2);
+			DEBUG_INIT_C("fab_opt", fab_opt, 2);
+			DEBUG_INIT_C("cpu_freq", cpu_freq, 2);
+		} else if (hclk_time_ps == 0) {
+			DEBUG_INIT_C("hclk_time_ps", hclk_time_ps, 2);
+			DEBUG_INIT_C("fab_opt", fab_opt, 2);
+			DEBUG_INIT_C("cpu_freq", cpu_freq, 2);
+		}
+
+		return MV_DDR3_TRAINING_ERR_BAD_SAR;
+	}
+
+#if defined(ECC_SUPPORT)
+	scrub_offs = U_BOOT_START_ADDR;
+	scrub_size = U_BOOT_SCRUB_SIZE;
+#else
+	scrub_offs = 0;
+	scrub_size = 0;
+#endif
+
+#if defined(ECC_SUPPORT) && defined(AUTO_DETECTION_SUPPORT)
+	ecc = DRAM_ECC;
+#endif
+
+#if defined(ECC_SUPPORT) && defined(AUTO_DETECTION_SUPPORT)
+	ecc = 0;
+	if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_ECC))
+		ecc = 1;
+#endif
+
+#ifdef DQS_CLK_ALIGNED
+	dqs_clk_aligned = 1;
+#endif
+
+	/* Check if DRAM is already initialized  */
+	if (reg_read(REG_BOOTROM_ROUTINE_ADDR) &
+	    (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS)) {
+		DEBUG_INIT_S("DDR3 Training Sequence - 2nd boot - Skip\n");
+		return MV_OK;
+	}
+
+	/*
+	 * Stage 1 - Dunit Setup
+	 */
+
+#ifdef DUNIT_STATIC
+	/*
+	 * For Static D-Unit Setup use must set the correct static values
+	 * at the ddr3_*soc*_vars.h file
+	 */
+	DEBUG_INIT_FULL_S("DDR3 Training Sequence - Static MC Init\n");
+	ddr3_static_mc_init();
+
+#ifdef ECC_SUPPORT
+	ecc = DRAM_ECC;
+	if (ecc) {
+		reg = reg_read(REG_SDRAM_CONFIG_ADDR);
+		reg |= (1 << REG_SDRAM_CONFIG_ECC_OFFS);
+		reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+	}
+#endif
+#endif
+
+#if defined(MV88F78X60) || defined(MV88F672X)
+#if defined(AUTO_DETECTION_SUPPORT)
+	/*
+	 * Configurations for both static and dynamic MC setups
+	 *
+	 * Dynamically Set 32Bit and ECC for AXP (Relevant only for
+	 * Marvell DB boards)
+	 */
+	if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_BUS_WIDTH)) {
+		ddr_width = 32;
+		DEBUG_INIT_S("DDR3 Training Sequence - DRAM bus width 32Bit\n");
+	}
+#endif
+
+#if defined(MV88F672X)
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR);
+	if ((reg >> 15) & 1)
+		ddr_width = 32;
+	else
+		ddr_width = 16;
+#endif
+#endif
+
+#ifdef DUNIT_SPD
+	status = ddr3_dunit_setup(ecc, hclk_time_ps, &ddr_width);
+	if (MV_OK != status) {
+		DEBUG_INIT_S("DDR3 Training Sequence - FAILED (ddr3 Dunit Setup)\n");
+		return status;
+	}
+#endif
+
+	/* Fix read ready phases for all SOC in reg 0x15C8 */
+	reg = reg_read(REG_TRAINING_DEBUG_3_ADDR);
+	reg &= ~(REG_TRAINING_DEBUG_3_MASK);
+	reg |= 0x4;		/* Phase 0 */
+	reg &= ~(REG_TRAINING_DEBUG_3_MASK << REG_TRAINING_DEBUG_3_OFFS);
+	reg |= (0x4 << (1 * REG_TRAINING_DEBUG_3_OFFS));	/* Phase 1 */
+	reg &= ~(REG_TRAINING_DEBUG_3_MASK << (3 * REG_TRAINING_DEBUG_3_OFFS));
+	reg |= (0x6 << (3 * REG_TRAINING_DEBUG_3_OFFS));	/* Phase 3 */
+	reg &= ~(REG_TRAINING_DEBUG_3_MASK << (4 * REG_TRAINING_DEBUG_3_OFFS));
+	reg |= (0x6 << (4 * REG_TRAINING_DEBUG_3_OFFS));
+	reg &= ~(REG_TRAINING_DEBUG_3_MASK << (5 * REG_TRAINING_DEBUG_3_OFFS));
+	reg |= (0x6 << (5 * REG_TRAINING_DEBUG_3_OFFS));
+	reg_write(REG_TRAINING_DEBUG_3_ADDR, reg);
+
+#if defined(MV88F672X)
+	/*
+	 * AxiBrespMode[8] = Compliant,
+	 * AxiAddrDecodeCntrl[11] = Internal,
+	 * AxiDataBusWidth[0] = 128bit
+	 */
+	/* 0x14A8 - AXI Control Register */
+	reg_write(REG_DRAM_AXI_CTRL_ADDR, 0);
+#else
+	/* 0x14A8 - AXI Control Register */
+	reg_write(REG_DRAM_AXI_CTRL_ADDR, 0x00000100);
+	reg_write(REG_CDI_CONFIG_ADDR, 0x00000006);
+
+	if ((ddr_width == 64) && (reg_read(REG_DDR_IO_ADDR) &
+				  (1 << REG_DDR_IO_CLK_RATIO_OFFS))) {
+		/* 0x14A8 - AXI Control Register */
+		reg_write(REG_DRAM_AXI_CTRL_ADDR, 0x00000101);
+		reg_write(REG_CDI_CONFIG_ADDR, 0x00000007);
+	}
+#endif
+
+#if !defined(MV88F67XX)
+	/*
+	 * ARMADA-370 activate DLB later at the u-boot,
+	 * Armada38x - No DLB activation at this time
+	 */
+	reg_write(DLB_BUS_OPTIMIZATION_WEIGHTS_REG, 0x18C01E);
+
+#if defined(MV88F78X60)
+	/* WA according to eratta GL-8672902*/
+	if (mv_ctrl_rev_get() == MV_78XX0_B0_REV)
+		reg_write(DLB_BUS_OPTIMIZATION_WEIGHTS_REG, 0xc19e);
+#endif
+
+	reg_write(DLB_AGING_REGISTER, 0x0f7f007f);
+	reg_write(DLB_EVICTION_CONTROL_REG, 0x0);
+	reg_write(DLB_EVICTION_TIMERS_REGISTER_REG, 0x00FF3C1F);
+
+	reg_write(MBUS_UNITS_PRIORITY_CONTROL_REG, 0x55555555);
+	reg_write(FABRIC_UNITS_PRIORITY_CONTROL_REG, 0xAA);
+	reg_write(MBUS_UNITS_PREFETCH_CONTROL_REG, 0xffff);
+	reg_write(FABRIC_UNITS_PREFETCH_CONTROL_REG, 0xf0f);
+
+#if defined(MV88F78X60)
+	/* WA according to eratta GL-8672902 */
+	if (mv_ctrl_rev_get() == MV_78XX0_B0_REV) {
+		reg = reg_read(REG_STATIC_DRAM_DLB_CONTROL);
+		reg |= DLB_ENABLE;
+		reg_write(REG_STATIC_DRAM_DLB_CONTROL, reg);
+	}
+#endif /* end defined(MV88F78X60) */
+#endif /* end !defined(MV88F67XX) */
+
+	if (ddr3_get_log_level() >= MV_LOG_LEVEL_1)
+		print_dunit_setup();
+
+	/*
+	 * Stage 2 - Training Values Setup
+	 */
+#ifdef STATIC_TRAINING
+	/*
+	 * DRAM Init - After all the D-unit values are set, its time to init
+	 * the D-unit
+	 */
+	/* Wait for '0' */
+	reg_write(REG_SDRAM_INIT_CTRL_ADDR, 0x1);
+	do {
+		reg = (reg_read(REG_SDRAM_INIT_CTRL_ADDR)) &
+			(1 << REG_SDRAM_INIT_CTRL_OFFS);
+	} while (reg);
+
+	/* ddr3 init using static parameters - HW training is disabled */
+	DEBUG_INIT_FULL_S("DDR3 Training Sequence - Static Training Parameters\n");
+	ddr3_static_training_init();
+
+#if defined(MV88F78X60)
+	/*
+	 * If ECC is enabled, need to scrub the U-Boot area memory region -
+	 * Run training function with Xor bypass just to scrub the memory
+	 */
+	status = ddr3_hw_training(target_freq, ddr_width,
+				  1, scrub_offs, scrub_size,
+				  dqs_clk_aligned, DDR3_TRAINING_DEBUG,
+				  REG_DIMM_SKIP_WL);
+	if (MV_OK != status) {
+		DEBUG_INIT_FULL_S("DDR3 Training Sequence - FAILED\n");
+		return status;
+	}
+#endif
+#else
+	/* Set X-BAR windows for the training sequence */
+	ddr3_save_and_set_training_windows(win_backup);
+
+	/* Run DDR3 Training Sequence */
+	/* DRAM Init */
+	reg_write(REG_SDRAM_INIT_CTRL_ADDR, 0x1);
+	do {
+		reg = (reg_read(REG_SDRAM_INIT_CTRL_ADDR)) &
+			(1 << REG_SDRAM_INIT_CTRL_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	/* ddr3 init using DDR3 HW training procedure */
+	DEBUG_INIT_FULL_S("DDR3 Training Sequence - HW Training Procedure\n");
+	status = ddr3_hw_training(target_freq, ddr_width,
+				  0, scrub_offs, scrub_size,
+				  dqs_clk_aligned, DDR3_TRAINING_DEBUG,
+				  REG_DIMM_SKIP_WL);
+	if (MV_OK != status) {
+		DEBUG_INIT_FULL_S("DDR3 Training Sequence - FAILED\n");
+		return status;
+	}
+#endif
+
+	/*
+	 * Stage 3 - Finish
+	 */
+#if defined(MV88F78X60) || defined(MV88F672X)
+	/* Disable ECC Ignore bit */
+	reg = reg_read(REG_SDRAM_CONFIG_ADDR) &
+		~(1 << REG_SDRAM_CONFIG_IERR_OFFS);
+	reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+#endif
+
+#if !defined(STATIC_TRAINING)
+	/* Restore and set windows */
+	ddr3_restore_and_set_final_windows(win_backup);
+#endif
+
+	/* Update DRAM init indication in bootROM register */
+	reg = reg_read(REG_BOOTROM_ROUTINE_ADDR);
+	reg_write(REG_BOOTROM_ROUTINE_ADDR,
+		  reg | (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS));
+
+#if !defined(MV88F67XX)
+#if defined(MV88F78X60)
+	if (mv_ctrl_rev_get() == MV_78XX0_B0_REV) {
+		reg = reg_read(REG_SDRAM_CONFIG_ADDR);
+		if (ecc == 0)
+			reg_write(REG_SDRAM_CONFIG_ADDR, reg | (1 << 19));
+	}
+#endif /* end defined(MV88F78X60) */
+
+	reg_write(DLB_EVICTION_CONTROL_REG, 0x9);
+
+	reg = reg_read(REG_STATIC_DRAM_DLB_CONTROL);
+	reg |= (DLB_ENABLE | DLB_WRITE_COALESING | DLB_AXI_PREFETCH_EN |
+		DLB_MBUS_PREFETCH_EN | PREFETCH_NLNSZTR);
+	reg_write(REG_STATIC_DRAM_DLB_CONTROL, reg);
+#endif /* end !defined(MV88F67XX) */
+
+#ifdef STATIC_TRAINING
+	DEBUG_INIT_S("DDR3 Training Sequence - Ended Successfully (S)\n");
+#else
+	DEBUG_INIT_S("DDR3 Training Sequence - Ended Successfully\n");
+#endif
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_get_cpu_freq
+ * Desc:     read S@R and return CPU frequency
+ * Args:
+ * Notes:
+ * Returns:  required value
+ */
+
+u32 ddr3_get_cpu_freq(void)
+{
+	u32 reg, cpu_freq;
+
+#if defined(MV88F672X)
+	/* Read sample at reset setting */
+	reg = reg_read(REG_SAMPLE_RESET_HIGH_ADDR);	/* 0xE8200 */
+	cpu_freq = (reg & REG_SAMPLE_RESET_CPU_FREQ_MASK) >>
+		REG_SAMPLE_RESET_CPU_FREQ_OFFS;
+#else
+	/* Read sample at reset setting */
+	reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR);	/* 0x18230 [23:21] */
+#if defined(MV88F78X60)
+	cpu_freq = (reg & REG_SAMPLE_RESET_CPU_FREQ_MASK) >>
+		REG_SAMPLE_RESET_CPU_FREQ_OFFS;
+	reg = reg_read(REG_SAMPLE_RESET_HIGH_ADDR);	/* 0x18234 [20] */
+	cpu_freq |= (((reg >> REG_SAMPLE_RESET_HIGH_CPU_FREQ_OFFS) & 0x1) << 3);
+#elif defined(MV88F67XX)
+	cpu_freq = (reg & REG_SAMPLE_RESET_CPU_FREQ_MASK) >>
+		REG_SAMPLE_RESET_CPU_FREQ_OFFS;
+#endif
+#endif
+
+	return cpu_freq;
+}
+
+/*
+ * Name:     ddr3_get_fab_opt
+ * Desc:     read S@R and return CPU frequency
+ * Args:
+ * Notes:
+ * Returns:  required value
+ */
+u32 ddr3_get_fab_opt(void)
+{
+	__maybe_unused u32 reg, fab_opt;
+
+#if defined(MV88F672X)
+	return 0;		/* No fabric */
+#else
+	/* Read sample at reset setting */
+	reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR);
+	fab_opt = (reg & REG_SAMPLE_RESET_FAB_MASK) >>
+		REG_SAMPLE_RESET_FAB_OFFS;
+
+#if defined(MV88F78X60)
+	reg = reg_read(REG_SAMPLE_RESET_HIGH_ADDR);
+	fab_opt |= (((reg >> 19) & 0x1) << 4);
+#endif
+
+	return fab_opt;
+#endif
+}
+
+/*
+ * Name:     ddr3_get_vco_freq
+ * Desc:     read S@R and return VCO frequency
+ * Args:
+ * Notes:
+ * Returns:  required value
+ */
+u32 ddr3_get_vco_freq(void)
+{
+	u32 fab, cpu_freq, ui_vco_freq;
+
+	fab = ddr3_get_fab_opt();
+	cpu_freq = ddr3_get_cpu_freq();
+
+	if (fab == 2 || fab == 3 || fab == 7 || fab == 8 || fab == 10 ||
+	    fab == 15 || fab == 17 || fab == 20)
+		ui_vco_freq = cpu_freq + CLK_CPU;
+	else
+		ui_vco_freq = cpu_freq;
+
+	return ui_vco_freq;
+}
+
+#ifdef STATIC_TRAINING
+/*
+ * Name:     ddr3_static_training_init - Init DDR3 Training with
+ *           static parameters
+ * Desc:     Use this routine to init the controller without the HW training
+ *           procedure
+ *           User must provide compatible header file with registers data.
+ * Args:     None.
+ * Notes:
+ * Returns:  None.
+ */
+void ddr3_static_training_init(void)
+{
+	MV_DRAM_MODES *ddr_mode;
+	u32 reg;
+	int j;
+
+	ddr_mode = ddr3_get_static_ddr_mode();
+
+	j = 0;
+	while (ddr_mode->vals[j].reg_addr != 0) {
+		udelay(10);	/* haim want to delay each write */
+		reg_write(ddr_mode->vals[j].reg_addr,
+			  ddr_mode->vals[j].reg_value);
+
+		if (ddr_mode->vals[j].reg_addr ==
+		    REG_PHY_REGISTRY_FILE_ACCESS_ADDR)
+			do {
+				reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
+					REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+			} while (reg);
+		j++;
+	}
+}
+#endif
+
+/*
+ * Name:     ddr3_get_static_mc_value - Init Memory controller with static
+ *           parameters
+ * Desc:     Use this routine to init the controller without the HW training
+ *           procedure
+ *           User must provide compatible header file with registers data.
+ * Args:     None.
+ * Notes:
+ * Returns:  None.
+ */
+u32 ddr3_get_static_mc_value(u32 reg_addr, u32 offset1, u32 mask1, u32 offset2,
+			     u32 mask2)
+{
+	u32 reg, tmp;
+
+	reg = reg_read(reg_addr);
+
+	tmp = (reg >> offset1) & mask1;
+	if (mask2)
+		tmp |= (reg >> offset2) & mask2;
+
+	return tmp;
+}
+
+/*
+ * Name:     ddr3_get_static_ddr_mode - Init Memory controller with static
+ *           parameters
+ * Desc:     Use this routine to init the controller without the HW training
+ *           procedure
+ *           User must provide compatible header file with registers data.
+ * Args:     None.
+ * Notes:
+ * Returns:  None.
+ */
+__weak MV_DRAM_MODES *ddr3_get_static_ddr_mode(void)
+{
+	u32 chip_board_rev, i;
+	u32 size;
+
+	/* Do not modify this code. relevant only for marvell Boards */
+#if defined(DB_78X60_PCAC)
+	chip_board_rev = Z1_PCAC;
+#elif defined(DB_78X60_AMC)
+	chip_board_rev = A0_AMC;
+#elif defined(DB_88F6710_PCAC)
+	chip_board_rev = A0_PCAC;
+#elif defined(RD_88F6710)
+	chip_board_rev = A0_RD;
+#elif defined(MV88F672X)
+	chip_board_rev = mv_board_id_get();
+#else
+	chip_board_rev = A0;
+#endif
+
+	size = sizeof(ddr_modes) / sizeof(MV_DRAM_MODES);
+	for (i = 0; i < size; i++) {
+		if ((ddr3_get_cpu_freq() == ddr_modes[i].cpu_freq) &&
+		    (ddr3_get_fab_opt() == ddr_modes[i].fab_freq) &&
+		    (chip_board_rev == ddr_modes[i].chip_board_rev))
+			return &ddr_modes[i];
+	}
+
+	return &ddr_modes[0];
+}
+
+#ifdef DUNIT_STATIC
+/*
+ * Name:     ddr3_static_mc_init - Init Memory controller with static parameters
+ * Desc:     Use this routine to init the controller without the HW training
+ *           procedure
+ *           User must provide compatible header file with registers data.
+ * Args:     None.
+ * Notes:
+ * Returns:  None.
+ */
+void ddr3_static_mc_init(void)
+{
+	MV_DRAM_MODES *ddr_mode;
+	u32 reg;
+	int j;
+
+	ddr_mode = ddr3_get_static_ddr_mode();
+	j = 0;
+	while (ddr_mode->regs[j].reg_addr != 0) {
+		reg_write(ddr_mode->regs[j].reg_addr,
+			  ddr_mode->regs[j].reg_value);
+		if (ddr_mode->regs[j].reg_addr ==
+		    REG_PHY_REGISTRY_FILE_ACCESS_ADDR)
+			do {
+				reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
+					REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+			} while (reg);
+		j++;
+	}
+}
+#endif
+
+/*
+ * Name:     ddr3_check_config - Check user configurations: ECC/MultiCS
+ * Desc:
+ * Args:     twsi Address
+ * Notes:    Only Available for ArmadaXP/Armada 370 DB boards
+ * Returns:  None.
+ */
+int ddr3_check_config(u32 twsi_addr, MV_CONFIG_TYPE config_type)
+{
+#ifdef AUTO_DETECTION_SUPPORT
+	u8 data = 0;
+	int ret;
+	int offset;
+
+	if ((config_type == CONFIG_ECC) || (config_type == CONFIG_BUS_WIDTH))
+		offset = 1;
+	else
+		offset = 0;
+
+	ret = i2c_read(twsi_addr, offset, 1, (u8 *)&data, 1);
+	if (!ret) {
+		switch (config_type) {
+		case CONFIG_ECC:
+			if (data & 0x2)
+				return 1;
+			break;
+		case CONFIG_BUS_WIDTH:
+			if (data & 0x1)
+				return 1;
+			break;
+#ifdef DB_88F6710
+		case CONFIG_MULTI_CS:
+			if (CFG_MULTI_CS_MODE(data))
+				return 1;
+			break;
+#else
+		case CONFIG_MULTI_CS:
+			break;
+#endif
+		}
+	}
+#endif
+
+	return 0;
+}
+
+#if defined(DB_88F78X60_REV2)
+/*
+ * Name:     ddr3_get_eprom_fabric - Get Fabric configuration from EPROM
+ * Desc:
+ * Args:     twsi Address
+ * Notes:    Only Available for ArmadaXP DB Rev2 boards
+ * Returns:  None.
+ */
+u8 ddr3_get_eprom_fabric(void)
+{
+#ifdef AUTO_DETECTION_SUPPORT
+	u8 data = 0;
+	int ret;
+
+	ret = i2c_read(NEW_FABRIC_TWSI_ADDR, 1, 1, (u8 *)&data, 1);
+	if (!ret)
+		return data & 0x1F;
+#endif
+
+	return 0;
+}
+
+#endif
+
+/*
+ * Name:     ddr3_cl_to_valid_cl - this return register matching CL value
+ * Desc:
+ * Args:     clValue - the value
+
+ * Notes:
+ * Returns:  required CL value
+ */
+u32 ddr3_cl_to_valid_cl(u32 cl)
+{
+	switch (cl) {
+	case 5:
+		return 2;
+		break;
+	case 6:
+		return 4;
+		break;
+	case 7:
+		return 6;
+		break;
+	case 8:
+		return 8;
+		break;
+	case 9:
+		return 10;
+		break;
+	case 10:
+		return 12;
+		break;
+	case 11:
+		return 14;
+		break;
+	case 12:
+		return 1;
+		break;
+	case 13:
+		return 3;
+		break;
+	case 14:
+		return 5;
+		break;
+	default:
+		return 2;
+	}
+}
+
+/*
+ * Name:     ddr3_cl_to_valid_cl - this return register matching CL value
+ * Desc:
+ * Args:     clValue - the value
+ * Notes:
+ * Returns:  required CL value
+ */
+u32 ddr3_valid_cl_to_cl(u32 ui_valid_cl)
+{
+	switch (ui_valid_cl) {
+	case 1:
+		return 12;
+		break;
+	case 2:
+		return 5;
+		break;
+	case 3:
+		return 13;
+		break;
+	case 4:
+		return 6;
+		break;
+	case 5:
+		return 14;
+		break;
+	case 6:
+		return 7;
+		break;
+	case 8:
+		return 8;
+		break;
+	case 10:
+		return 9;
+		break;
+	case 12:
+		return 10;
+		break;
+	case 14:
+		return 11;
+		break;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * Name:     ddr3_get_cs_num_from_reg
+ * Desc:
+ * Args:
+ * Notes:
+ * Returns:
+ */
+u32 ddr3_get_cs_num_from_reg(void)
+{
+	u32 cs_ena = ddr3_get_cs_ena_from_reg();
+	u32 cs_count = 0;
+	u32 cs;
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs))
+			cs_count++;
+	}
+
+	return cs_count;
+}
+
+/*
+ * Name:     ddr3_get_cs_ena_from_reg
+ * Desc:
+ * Args:
+ * Notes:
+ * Returns:
+ */
+u32 ddr3_get_cs_ena_from_reg(void)
+{
+	return reg_read(REG_DDR3_RANK_CTRL_ADDR) &
+		REG_DDR3_RANK_CTRL_CS_ENA_MASK;
+}
+
+/*
+ * mv_ctrl_rev_get - Get Marvell controller device revision number
+ *
+ * DESCRIPTION:
+ *       This function returns 8bit describing the device revision as defined
+ *       in PCI Express Class Code and Revision ID Register.
+ *
+ * INPUT:
+ *       None.
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       8bit desscribing Marvell controller revision number
+ *
+ */
+#if !defined(MV88F672X)
+u8 mv_ctrl_rev_get(void)
+{
+	u8 rev_num;
+
+#if defined(MV_INCLUDE_CLK_PWR_CNTRL)
+	/* Check pex power state */
+	u32 pex_power;
+	pex_power = mv_ctrl_pwr_clck_get(PEX_UNIT_ID, 0);
+	if (pex_power == 0)
+		mv_ctrl_pwr_clck_set(PEX_UNIT_ID, 0, 1);
+#endif
+	rev_num = (u8)reg_read(PEX_CFG_DIRECT_ACCESS(0,
+			PCI_CLASS_CODE_AND_REVISION_ID));
+
+#if defined(MV_INCLUDE_CLK_PWR_CNTRL)
+	/* Return to power off state */
+	if (pex_power == 0)
+		mv_ctrl_pwr_clck_set(PEX_UNIT_ID, 0, 0);
+#endif
+
+	return (rev_num & PCCRIR_REVID_MASK) >> PCCRIR_REVID_OFFS;
+}
+
+#endif
+
+#if defined(MV88F672X)
+void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps)
+{
+	u32 tmp, hclk;
+
+	switch (freq_mode) {
+	case CPU_333MHz_DDR_167MHz_L2_167MHz:
+		hclk = 84;
+		tmp = DDR_100;
+		break;
+	case CPU_266MHz_DDR_266MHz_L2_133MHz:
+	case CPU_333MHz_DDR_222MHz_L2_167MHz:
+	case CPU_400MHz_DDR_200MHz_L2_200MHz:
+	case CPU_400MHz_DDR_267MHz_L2_200MHz:
+	case CPU_533MHz_DDR_267MHz_L2_267MHz:
+	case CPU_500MHz_DDR_250MHz_L2_250MHz:
+	case CPU_600MHz_DDR_300MHz_L2_300MHz:
+	case CPU_800MHz_DDR_267MHz_L2_400MHz:
+	case CPU_900MHz_DDR_300MHz_L2_450MHz:
+		tmp = DDR_300;
+		hclk = 150;
+		break;
+	case CPU_333MHz_DDR_333MHz_L2_167MHz:
+	case CPU_500MHz_DDR_334MHz_L2_250MHz:
+	case CPU_666MHz_DDR_333MHz_L2_333MHz:
+		tmp = DDR_333;
+		hclk = 165;
+		break;
+	case CPU_533MHz_DDR_356MHz_L2_267MHz:
+		tmp = DDR_360;
+		hclk = 180;
+		break;
+	case CPU_400MHz_DDR_400MHz_L2_200MHz:
+	case CPU_600MHz_DDR_400MHz_L2_300MHz:
+	case CPU_800MHz_DDR_400MHz_L2_400MHz:
+	case CPU_400MHz_DDR_400MHz_L2_400MHz:
+		tmp = DDR_400;
+		hclk = 200;
+		break;
+	case CPU_666MHz_DDR_444MHz_L2_333MHz:
+	case CPU_900MHz_DDR_450MHz_L2_450MHz:
+		tmp = DDR_444;
+		hclk = 222;
+		break;
+	case CPU_500MHz_DDR_500MHz_L2_250MHz:
+	case CPU_1000MHz_DDR_500MHz_L2_500MHz:
+	case CPU_1000MHz_DDR_500MHz_L2_333MHz:
+		tmp = DDR_500;
+		hclk = 250;
+		break;
+	case CPU_533MHz_DDR_533MHz_L2_267MHz:
+	case CPU_800MHz_DDR_534MHz_L2_400MHz:
+	case CPU_1100MHz_DDR_550MHz_L2_550MHz:
+		tmp = DDR_533;
+		hclk = 267;
+		break;
+	case CPU_600MHz_DDR_600MHz_L2_300MHz:
+	case CPU_900MHz_DDR_600MHz_L2_450MHz:
+	case CPU_1200MHz_DDR_600MHz_L2_600MHz:
+		tmp = DDR_600;
+		hclk = 300;
+		break;
+	case CPU_666MHz_DDR_666MHz_L2_333MHz:
+	case CPU_1000MHz_DDR_667MHz_L2_500MHz:
+		tmp = DDR_666;
+		hclk = 333;
+		break;
+	default:
+		*ddr_freq = 0;
+		*hclk_ps = 0;
+		break;
+	}
+
+	*ddr_freq = tmp;		/* DDR freq define */
+	*hclk_ps = 1000000 / hclk;	/* values are 1/HCLK in ps */
+
+	return;
+}
+#endif
diff --git a/drivers/ddr/marvell/axp/ddr3_init.h b/drivers/ddr/marvell/axp/ddr3_init.h
new file mode 100644
index 0000000..569a14b
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_init.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __DDR3_INIT_H
+#define __DDR3_INIT_H
+
+/*
+ * Debug
+ */
+
+/*
+ * MV_DEBUG_INIT need to be defines, otherwise the output of the
+ * DDR2 training code is not complete and misleading
+ */
+#define MV_DEBUG_INIT
+
+#ifdef MV_DEBUG_INIT
+#define DEBUG_INIT_S(s)			puts(s)
+#define DEBUG_INIT_D(d, l)		printf("%x", d)
+#define DEBUG_INIT_D_10(d, l)		printf("%d", d)
+#else
+#define DEBUG_INIT_S(s)
+#define DEBUG_INIT_D(d, l)
+#define DEBUG_INIT_D_10(d, l)
+#endif
+
+#ifdef MV_DEBUG_INIT_FULL
+#define DEBUG_INIT_FULL_S(s)		puts(s)
+#define DEBUG_INIT_FULL_D(d, l)		printf("%x", d)
+#define DEBUG_INIT_FULL_D_10(d, l)	printf("%d", d)
+#define DEBUG_WR_REG(reg, val) \
+	{ DEBUG_INIT_S("Write Reg: 0x"); DEBUG_INIT_D((reg), 8); \
+	  DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); }
+#define DEBUG_RD_REG(reg, val) \
+	{ DEBUG_INIT_S("Read  Reg: 0x"); DEBUG_INIT_D((reg), 8); \
+	  DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); }
+#else
+#define DEBUG_INIT_FULL_S(s)
+#define DEBUG_INIT_FULL_D(d, l)
+#define DEBUG_INIT_FULL_D_10(d, l)
+#define DEBUG_WR_REG(reg, val)
+#define DEBUG_RD_REG(reg, val)
+#endif
+
+#define DEBUG_INIT_FULL_C(s, d, l) \
+	{ DEBUG_INIT_FULL_S(s); DEBUG_INIT_FULL_D(d, l); DEBUG_INIT_FULL_S("\n"); }
+#define DEBUG_INIT_C(s, d, l) \
+	{ DEBUG_INIT_S(s); DEBUG_INIT_D(d, l); DEBUG_INIT_S("\n"); }
+
+#define MV_MBUS_REGS_OFFSET                 (0x20000)
+
+#include "ddr3_hw_training.h"
+
+#define MAX_DIMM_NUM			2
+#define SPD_SIZE			128
+
+#ifdef MV88F78X60
+#include "ddr3_axp.h"
+#elif defined(MV88F67XX)
+#include "ddr3_a370.h"
+#elif defined(MV88F672X)
+#include "ddr3_a375.h"
+#endif
+
+/* DRR training Error codes */
+/* Stage 0 errors */
+#define MV_DDR3_TRAINING_ERR_BAD_SAR			0xDD300001
+/* Stage 1 errors */
+#define MV_DDR3_TRAINING_ERR_TWSI_FAIL			0xDD301001
+#define MV_DDR3_TRAINING_ERR_DIMM_TYPE_NO_MATCH		0xDD301001
+#define MV_DDR3_TRAINING_ERR_TWSI_BAD_TYPE		0xDD301003
+#define MV_DDR3_TRAINING_ERR_BUS_WIDTH_NOT_MATCH	0xDD301004
+#define MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP		0xDD301005
+#define MV_DDR3_TRAINING_ERR_MAX_CS_LIMIT		0xDD301006
+#define MV_DDR3_TRAINING_ERR_MAX_ENA_CS_LIMIT		0xDD301007
+#define MV_DDR3_TRAINING_ERR_BAD_R_DIMM_SETUP		0xDD301008
+/* Stage 2 errors */
+#define MV_DDR3_TRAINING_ERR_HW_FAIL_BASE		0xDD302000
+
+typedef enum config_type {
+	CONFIG_ECC,
+	CONFIG_MULTI_CS,
+	CONFIG_BUS_WIDTH
+} MV_CONFIG_TYPE;
+
+enum log_level  {
+	MV_LOG_LEVEL_0,
+	MV_LOG_LEVEL_1,
+	MV_LOG_LEVEL_2,
+	MV_LOG_LEVEL_3
+};
+
+int ddr3_hw_training(u32 target_freq, u32 ddr_width,
+		     int xor_bypass, u32 scrub_offs, u32 scrub_size,
+		     int dqs_clk_aligned, int debug_mode, int reg_dimm_skip_wl);
+
+void ddr3_print_version(void);
+void fix_pll_val(u8 target_fab);
+u8 ddr3_get_eprom_fabric(void);
+u32 ddr3_get_fab_opt(void);
+u32 ddr3_get_cpu_freq(void);
+u32 ddr3_get_vco_freq(void);
+int ddr3_check_config(u32 addr, MV_CONFIG_TYPE config_type);
+u32 ddr3_get_static_mc_value(u32 reg_addr, u32 offset1, u32 mask1, u32 offset2,
+			     u32 mask2);
+u32 ddr3_cl_to_valid_cl(u32 cl);
+u32 ddr3_valid_cl_to_cl(u32 ui_valid_cl);
+u32 ddr3_get_cs_num_from_reg(void);
+u32 ddr3_get_cs_ena_from_reg(void);
+u8 mv_ctrl_rev_get(void);
+
+u32 ddr3_get_log_level(void);
+
+/* SPD */
+int ddr3_dunit_setup(u32 ecc_ena, u32 hclk_time, u32 *ddr_width);
+
+/*
+ * Accessor functions for the registers
+ */
+static inline void reg_write(u32 addr, u32 val)
+{
+	writel(val, INTER_REGS_BASE + addr);
+}
+
+static inline u32 reg_read(u32 addr)
+{
+	return readl(INTER_REGS_BASE + addr);
+}
+
+static inline void reg_bit_set(u32 addr, u32 mask)
+{
+	setbits_le32(INTER_REGS_BASE + addr, mask);
+}
+
+static inline void reg_bit_clr(u32 addr, u32 mask)
+{
+	clrbits_le32(INTER_REGS_BASE + addr, mask);
+}
+
+#endif /* __DDR3_INIT_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_patterns_64bit.h b/drivers/ddr/marvell/axp/ddr3_patterns_64bit.h
new file mode 100644
index 0000000..00dc9e3
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_patterns_64bit.h
@@ -0,0 +1,923 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __DDR3_PATTERNS_64_H
+#define __DDR3_PATTERNS_64_H
+
+/*
+ * Patterns Declerations
+ */
+
+u32 wl_sup_pattern[LEN_WL_SUP_PATTERN] __aligned(32) = {
+	0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d,
+	0x14131211, 0x18171615, 0x1c1b1a19, 0x201f1e1d,
+	0x24232221, 0x28272625, 0x2c2b2a29, 0x302f2e2d,
+	0x34333231, 0x38373635, 0x3c3b3a39, 0x403f3e3d,
+	0x44434241, 0x48474645, 0x4c4b4a49, 0x504f4e4d,
+	0x54535251, 0x58575655, 0x5c5b5a59, 0x605f5e5d,
+	0x64636261, 0x68676665, 0x6c6b6a69, 0x706f6e6d,
+	0x74737271, 0x78777675, 0x7c7b7a79, 0x807f7e7d
+};
+
+u32 pbs_pattern_32b[2][LEN_PBS_PATTERN] __aligned(32) = {
+	{
+		0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555,
+		0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555,
+		0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555,
+		0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555
+	},
+	{
+		0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA,
+		0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA,
+		0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA,
+		0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA
+	}
+};
+
+u32 pbs_pattern_64b[2][LEN_PBS_PATTERN] __aligned(32) = {
+	{
+		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555,
+		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555,
+		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555,
+		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555
+	},
+	{
+		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA,
+		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA,
+		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA,
+		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA
+	}
+};
+
+u32 rl_pattern[LEN_STD_PATTERN] __aligned(32) = {
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x01010101, 0x01010101, 0x01010101, 0x01010101
+};
+
+u32 killer_pattern_32b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = {
+	{
+		0x01010101, 0x00000000, 0x01010101, 0xFFFFFFFF,
+		0x01010101, 0x00000000, 0x01010101, 0xFFFFFFFF,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0xFEFEFEFE,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0xFEFEFEFE,
+		0x01010101, 0xFEFEFEFE, 0x01010101, 0x01010101,
+		0x01010101, 0xFEFEFEFE, 0x01010101, 0x01010101,
+		0xFEFEFEFE, 0x01010101, 0xFEFEFEFE, 0x00000000,
+		0xFEFEFEFE, 0x01010101, 0xFEFEFEFE, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x01010101,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x01010101,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xFEFEFEFE,
+		0x00000000, 0x00000000, 0x00000000, 0xFEFEFEFE,
+		0xFEFEFEFE, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFEFEFEFE, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFEFEFEFE, 0x00000000, 0xFEFEFEFE, 0x00000000,
+		0xFEFEFEFE, 0x00000000, 0xFEFEFEFE, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x00000000,
+		0x01010101, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x01010101, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE
+	},
+	{
+		0x02020202, 0x00000000, 0x02020202, 0xFFFFFFFF,
+		0x02020202, 0x00000000, 0x02020202, 0xFFFFFFFF,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0xFDFDFDFD,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0xFDFDFDFD,
+		0x02020202, 0xFDFDFDFD, 0x02020202, 0x02020202,
+		0x02020202, 0xFDFDFDFD, 0x02020202, 0x02020202,
+		0xFDFDFDFD, 0x02020202, 0xFDFDFDFD, 0x00000000,
+		0xFDFDFDFD, 0x02020202, 0xFDFDFDFD, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x02020202,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x02020202,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xFDFDFDFD,
+		0x00000000, 0x00000000, 0x00000000, 0xFDFDFDFD,
+		0xFDFDFDFD, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFDFDFDFD, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFDFDFDFD, 0x00000000, 0xFDFDFDFD, 0x00000000,
+		0xFDFDFDFD, 0x00000000, 0xFDFDFDFD, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x00000000,
+		0x02020202, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x02020202, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD
+	},
+	{
+		0x04040404, 0x00000000, 0x04040404, 0xFFFFFFFF,
+		0x04040404, 0x00000000, 0x04040404, 0xFFFFFFFF,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0xFBFBFBFB,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0xFBFBFBFB,
+		0x04040404, 0xFBFBFBFB, 0x04040404, 0x04040404,
+		0x04040404, 0xFBFBFBFB, 0x04040404, 0x04040404,
+		0xFBFBFBFB, 0x04040404, 0xFBFBFBFB, 0x00000000,
+		0xFBFBFBFB, 0x04040404, 0xFBFBFBFB, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x04040404,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x04040404,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xFBFBFBFB,
+		0x00000000, 0x00000000, 0x00000000, 0xFBFBFBFB,
+		0xFBFBFBFB, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFBFBFBFB, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFBFBFBFB, 0x00000000, 0xFBFBFBFB, 0x00000000,
+		0xFBFBFBFB, 0x00000000, 0xFBFBFBFB, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x00000000,
+		0x04040404, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x04040404, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB
+	},
+	{
+		0x08080808, 0x00000000, 0x08080808, 0xFFFFFFFF,
+		0x08080808, 0x00000000, 0x08080808, 0xFFFFFFFF,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0xF7F7F7F7,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0xF7F7F7F7,
+		0x08080808, 0xF7F7F7F7, 0x08080808, 0x08080808,
+		0x08080808, 0xF7F7F7F7, 0x08080808, 0x08080808,
+		0xF7F7F7F7, 0x08080808, 0xF7F7F7F7, 0x00000000,
+		0xF7F7F7F7, 0x08080808, 0xF7F7F7F7, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x08080808,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x08080808,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xF7F7F7F7,
+		0x00000000, 0x00000000, 0x00000000, 0xF7F7F7F7,
+		0xF7F7F7F7, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xF7F7F7F7, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xF7F7F7F7, 0x00000000, 0xF7F7F7F7, 0x00000000,
+		0xF7F7F7F7, 0x00000000, 0xF7F7F7F7, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x00000000,
+		0x08080808, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x08080808, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7
+	},
+	{
+		0x10101010, 0x00000000, 0x10101010, 0xFFFFFFFF,
+		0x10101010, 0x00000000, 0x10101010, 0xFFFFFFFF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0xEFEFEFEF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0xEFEFEFEF,
+		0x10101010, 0xEFEFEFEF, 0x10101010, 0x10101010,
+		0x10101010, 0xEFEFEFEF, 0x10101010, 0x10101010,
+		0xEFEFEFEF, 0x10101010, 0xEFEFEFEF, 0x00000000,
+		0xEFEFEFEF, 0x10101010, 0xEFEFEFEF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x10101010,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x10101010,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xEFEFEFEF,
+		0x00000000, 0x00000000, 0x00000000, 0xEFEFEFEF,
+		0xEFEFEFEF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xEFEFEFEF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xEFEFEFEF, 0x00000000, 0xEFEFEFEF, 0x00000000,
+		0xEFEFEFEF, 0x00000000, 0xEFEFEFEF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x00000000,
+		0x10101010, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x10101010, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF
+	},
+	{
+		0x20202020, 0x00000000, 0x20202020, 0xFFFFFFFF,
+		0x20202020, 0x00000000, 0x20202020, 0xFFFFFFFF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0xDFDFDFDF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0xDFDFDFDF,
+		0x20202020, 0xDFDFDFDF, 0x20202020, 0x20202020,
+		0x20202020, 0xDFDFDFDF, 0x20202020, 0x20202020,
+		0xDFDFDFDF, 0x20202020, 0xDFDFDFDF, 0x00000000,
+		0xDFDFDFDF, 0x20202020, 0xDFDFDFDF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x20202020,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x20202020,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xDFDFDFDF,
+		0x00000000, 0x00000000, 0x00000000, 0xDFDFDFDF,
+		0xDFDFDFDF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xDFDFDFDF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xDFDFDFDF, 0x00000000, 0xDFDFDFDF, 0x00000000,
+		0xDFDFDFDF, 0x00000000, 0xDFDFDFDF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x00000000,
+		0x20202020, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x20202020, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF
+	},
+	{
+		0x40404040, 0x00000000, 0x40404040, 0xFFFFFFFF,
+		0x40404040, 0x00000000, 0x40404040, 0xFFFFFFFF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0xBFBFBFBF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0xBFBFBFBF,
+		0x40404040, 0xBFBFBFBF, 0x40404040, 0x40404040,
+		0x40404040, 0xBFBFBFBF, 0x40404040, 0x40404040,
+		0xBFBFBFBF, 0x40404040, 0xBFBFBFBF, 0x00000000,
+		0xBFBFBFBF, 0x40404040, 0xBFBFBFBF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x40404040,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x40404040,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0xBFBFBFBF,
+		0x00000000, 0x00000000, 0x00000000, 0xBFBFBFBF,
+		0xBFBFBFBF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xBFBFBFBF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xBFBFBFBF, 0x00000000, 0xBFBFBFBF, 0x00000000,
+		0xBFBFBFBF, 0x00000000, 0xBFBFBFBF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x00000000,
+		0x40404040, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x40404040, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF
+	},
+	{
+		0x80808080, 0x00000000, 0x80808080, 0xFFFFFFFF,
+		0x80808080, 0x00000000, 0x80808080, 0xFFFFFFFF,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x7F7F7F7F,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x7F7F7F7F,
+		0x80808080, 0x7F7F7F7F, 0x80808080, 0x80808080,
+		0x80808080, 0x7F7F7F7F, 0x80808080, 0x80808080,
+		0x7F7F7F7F, 0x80808080, 0x7F7F7F7F, 0x00000000,
+		0x7F7F7F7F, 0x80808080, 0x7F7F7F7F, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x80808080,
+		0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x80808080,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x7F7F7F7F,
+		0x00000000, 0x00000000, 0x00000000, 0x7F7F7F7F,
+		0x7F7F7F7F, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x7F7F7F7F, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
+		0x7F7F7F7F, 0x00000000, 0x7F7F7F7F, 0x00000000,
+		0x7F7F7F7F, 0x00000000, 0x7F7F7F7F, 0x00000000,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080,
+		0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x00000000,
+		0x80808080, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x80808080, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F
+	}
+};
+
+u32 killer_pattern_64b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = {
+	{
+		0x01010101, 0x01010101, 0x00000000, 0x00000000,
+		0x01010101, 0x01010101, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x01010101, 0x01010101, 0x01010101, 0x01010101,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x01010101,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFEFEFEFE, 0xFEFEFEFE,
+		0xFEFEFEFE, 0xFEFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x01010101,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x01010101, 0x01010101, 0x00000000, 0x00000000,
+		0x01010101, 0x01010101, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE
+	},
+	{
+		0x02020202, 0x02020202, 0x00000000, 0x00000000,
+		0x02020202, 0x02020202, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x02020202, 0x02020202, 0x02020202, 0x02020202,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x02020202,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFDFDFDFD, 0xFDFDFDFD,
+		0xFDFDFDFD, 0xFDFDFDFD, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x02020202,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x02020202, 0x02020202, 0x00000000, 0x00000000,
+		0x02020202, 0x02020202, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD
+	},
+	{
+		0x04040404, 0x04040404, 0x00000000, 0x00000000,
+		0x04040404, 0x04040404, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x04040404, 0x04040404, 0x04040404, 0x04040404,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x04040404,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFBFBFBFB, 0xFBFBFBFB,
+		0xFBFBFBFB, 0xFBFBFBFB, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x04040404,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x04040404, 0x04040404, 0x00000000, 0x00000000,
+		0x04040404, 0x04040404, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB
+	},
+	{
+		0x08080808, 0x08080808, 0x00000000, 0x00000000,
+		0x08080808, 0x08080808, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x08080808, 0x08080808, 0x08080808, 0x08080808,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x08080808,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xF7F7F7F7, 0xF7F7F7F7,
+		0xF7F7F7F7, 0xF7F7F7F7, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x08080808,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x08080808, 0x08080808, 0x00000000, 0x00000000,
+		0x08080808, 0x08080808, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7
+	},
+	{
+		0x10101010, 0x10101010, 0x00000000, 0x00000000,
+		0x10101010, 0x10101010, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x10101010, 0x10101010, 0x10101010, 0x10101010,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x10101010,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xEFEFEFEF, 0xEFEFEFEF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x10101010,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x10101010, 0x10101010, 0x00000000, 0x00000000,
+		0x10101010, 0x10101010, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF
+	},
+	{
+		0x20202020, 0x20202020, 0x00000000, 0x00000000,
+		0x20202020, 0x20202020, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x20202020, 0x20202020, 0x20202020, 0x20202020,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x20202020,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xDFDFDFDF, 0xDFDFDFDF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x20202020,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x20202020, 0x20202020, 0x00000000, 0x00000000,
+		0x20202020, 0x20202020, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF
+	},
+	{
+		0x40404040, 0x40404040, 0x00000000, 0x00000000,
+		0x40404040, 0x40404040, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x40404040, 0x40404040, 0x40404040, 0x40404040,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x40404040,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xBFBFBFBF, 0xBFBFBFBF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x40404040,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x40404040, 0x40404040, 0x00000000, 0x00000000,
+		0x40404040, 0x40404040, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF
+	},
+	{
+		0x80808080, 0x80808080, 0x00000000, 0x00000000,
+		0x80808080, 0x80808080, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x80808080, 0x80808080, 0x80808080, 0x80808080,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x80808080,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x7F7F7F7F, 0x7F7F7F7F, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x80808080,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x80808080, 0x80808080, 0x00000000, 0x00000000,
+		0x80808080, 0x80808080, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F
+	}
+};
+
+u32 special_pattern[DQ_NUM][LEN_SPECIAL_PATTERN] __aligned(32) = {
+	{
+		0x00000000, 0x00000000, 0x01010101, 0x01010101,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101,
+		0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x01010101, 0x01010101, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x00000000, 0x00000000, 0xFEFEFEFE, 0xFEFEFEFE,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x01010101, 0x01010101, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x01010101,
+		0x00000000, 0x00000000, 0x01010101, 0x01010101,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE,
+		0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x02020202, 0x02020202,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202,
+		0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x02020202, 0x02020202, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x00000000, 0x00000000, 0xFDFDFDFD, 0xFDFDFDFD,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x02020202, 0x02020202, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x02020202,
+		0x00000000, 0x00000000, 0x02020202, 0x02020202,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD,
+		0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x04040404, 0x04040404,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404,
+		0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x04040404, 0x04040404, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x00000000, 0x00000000, 0xFBFBFBFB, 0xFBFBFBFB,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x04040404, 0x04040404, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x04040404,
+		0x00000000, 0x00000000, 0x04040404, 0x04040404,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB,
+		0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x08080808, 0x08080808,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808,
+		0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x08080808, 0x08080808, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x00000000, 0x00000000, 0xF7F7F7F7, 0xF7F7F7F7,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x08080808, 0x08080808, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x08080808,
+		0x00000000, 0x00000000, 0x08080808, 0x08080808,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7,
+		0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x10101010, 0x10101010,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010,
+		0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x10101010, 0x10101010, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x00000000, 0x00000000, 0xEFEFEFEF, 0xEFEFEFEF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x10101010, 0x10101010, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x10101010,
+		0x00000000, 0x00000000, 0x10101010, 0x10101010,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF,
+		0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x20202020, 0x20202020,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020,
+		0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x20202020, 0x20202020, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x00000000, 0x00000000, 0xDFDFDFDF, 0xDFDFDFDF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x20202020, 0x20202020, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x20202020,
+		0x00000000, 0x00000000, 0x20202020, 0x20202020,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF,
+		0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x40404040, 0x40404040,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040,
+		0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x40404040, 0x40404040, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x00000000, 0x00000000, 0xBFBFBFBF, 0xBFBFBFBF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x40404040, 0x40404040, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x40404040,
+		0x00000000, 0x00000000, 0x40404040, 0x40404040,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF,
+		0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000
+	},
+	{
+		0x00000000, 0x00000000, 0x80808080, 0x80808080,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080,
+		0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x80808080, 0x80808080, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x00000000, 0x00000000, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x80808080, 0x80808080, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x80808080,
+		0x00000000, 0x00000000, 0x80808080, 0x80808080,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F,
+		0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000
+	}
+};
+
+/* Fabric ratios table */
+u32 fabric_ratio[FAB_OPT] = {
+	0x04010204,
+	0x04020202,
+	0x08020306,
+	0x08020303,
+	0x04020303,
+	0x04020204,
+	0x04010202,
+	0x08030606,
+	0x08030505,
+	0x04020306,
+	0x0804050A,
+	0x04030606,
+	0x04020404,
+	0x04030306,
+	0x04020505,
+	0x08020505,
+	0x04010303,
+	0x08050A0A,
+	0x04030408,
+	0x04010102,
+	0x08030306
+};
+
+u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM] = {
+	{3, 2, 5, 7, 1, 0, 6, 4},
+	{2, 3, 6, 7, 1, 0, 4, 5},
+	{1, 3, 5, 6, 0, 2, 4, 7},
+	{0, 2, 4, 7, 1, 3, 5, 6},
+	{3, 0, 4, 6, 1, 2, 5, 7},
+	{0, 3, 5, 7, 1, 2, 4, 6},
+	{2, 3, 5, 7, 1, 0, 4, 6},
+	{0, 2, 5, 4, 1, 3, 6, 7},
+	{2, 3, 4, 7, 0, 1, 5, 6}
+};
+
+#endif /* __DDR3_PATTERNS_64_H */
diff --git a/drivers/ddr/marvell/axp/ddr3_pbs.c b/drivers/ddr/marvell/axp/ddr3_pbs.c
new file mode 100644
index 0000000..e44f08d
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_pbs.c
@@ -0,0 +1,1591 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_hw_training.h"
+
+/*
+ * Debug
+ */
+#define DEBUG_PBS_FULL_C(s, d, l) \
+	DEBUG_PBS_FULL_S(s); DEBUG_PBS_FULL_D(d, l); DEBUG_PBS_FULL_S("\n")
+#define DEBUG_PBS_C(s, d, l) \
+	DEBUG_PBS_S(s); DEBUG_PBS_D(d, l); DEBUG_PBS_S("\n")
+
+#ifdef MV_DEBUG_PBS
+#define DEBUG_PBS_S(s)			puts(s)
+#define DEBUG_PBS_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_PBS_S(s)
+#define DEBUG_PBS_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_FULL_PBS
+#define DEBUG_PBS_FULL_S(s)		puts(s)
+#define DEBUG_PBS_FULL_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_PBS_FULL_S(s)
+#define DEBUG_PBS_FULL_D(d, l)
+#endif
+
+#if defined(MV88F78X60) || defined(MV88F672X)
+
+/* Temp array for skew data storage */
+static u32 skew_array[(MAX_PUP_NUM) * DQ_NUM] = { 0 };
+
+/* PBS locked dq (per pup) */
+extern u32 pbs_locked_dq[MAX_PUP_NUM][DQ_NUM];
+extern u32 pbs_locked_dm[MAX_PUP_NUM];
+extern u32 pbs_locked_value[MAX_PUP_NUM][DQ_NUM];
+
+#if defined(MV88F672X)
+extern u32 pbs_pattern[2][LEN_16BIT_PBS_PATTERN];
+extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN];
+#else
+extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN];
+extern u32 pbs_pattern_64b[2][LEN_PBS_PATTERN];
+#endif
+
+extern u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM];
+
+static int ddr3_tx_shift_dqs_adll_step_before_fail(MV_DRAM_INFO *dram_info,
+		u32 cur_pup, u32 pbs_pattern_idx, u32 ecc);
+static int ddr3_rx_shift_dqs_to_first_fail(MV_DRAM_INFO *dram_info, u32 cur_pup,
+		u32 pbs_pattern_idx, u32 ecc);
+static int ddr3_pbs_per_bit(MV_DRAM_INFO *dram_info, int *start_over, int is_tx,
+		u32 *pcur_pup, u32 pbs_pattern_idx, u32 ecc);
+static int ddr3_set_pbs_results(MV_DRAM_INFO *dram_info, int is_tx);
+static void ddr3_pbs_write_pup_dqs_reg(u32 cs, u32 pup, u32 dqs_delay);
+
+/*
+ * Name:     ddr3_pbs_tx
+ * Desc:     Execute the PBS TX phase.
+ * Args:     dram_info   ddr3 training information struct
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_pbs_tx(MV_DRAM_INFO *dram_info)
+{
+	/* Array of Deskew results */
+
+	/*
+	 * Array to hold the total sum of skew from all iterations
+	 * (for average purpose)
+	 */
+	u32 skew_sum_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
+
+	/*
+	 * Array to hold the total average skew from both patterns
+	 * (for average purpose)
+	 */
+	u32 pattern_skew_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
+
+	u32 pbs_rep_time = 0;	/* counts number of loop in case of fail */
+	/* bit array for unlock pups - used to repeat on the RX operation */
+	u32 cur_pup;
+	u32 max_pup;
+	u32 pbs_retry;
+	u32 pup, dq, pups, cur_max_pup, valid_pup, reg;
+	u32 pattern_idx;
+	u32 ecc;
+	/* indicates whether we need to start the loop again */
+	int start_over;
+
+	DEBUG_PBS_S("DDR3 - PBS TX - Starting PBS TX procedure\n");
+
+	pups = dram_info->num_of_total_pups;
+	max_pup = dram_info->num_of_total_pups;
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_PBS_S("DDR3 - PBS RX - SW Override Enabled\n");
+
+	reg = 1 << REG_DRAM_TRAINING_AUTO_OFFS;
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	/* Running twice for 2 different patterns. each patterns - 3 times */
+	for (pattern_idx = 0; pattern_idx < COUNT_PBS_PATTERN; pattern_idx++) {
+		DEBUG_PBS_C("DDR3 - PBS TX - Working with pattern - ",
+			    pattern_idx, 1);
+
+		/* Reset sum array */
+		for (pup = 0; pup < pups; pup++) {
+			for (dq = 0; dq < DQ_NUM; dq++)
+				skew_sum_array[pup][dq] = 0;
+		}
+
+		/*
+		 * Perform PBS several of times (3 for each pattern).
+		 * At the end, we'll use the average
+		 */
+		/* If there is ECC, do each PBS again with mux change */
+		for (pbs_retry = 0; pbs_retry < COUNT_PBS_REPEAT; pbs_retry++) {
+			for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) {
+
+				/*
+				 * This parameter stores the current PUP
+				 * num - ecc mode dependent - 4-8 / 1 pups
+				 */
+				cur_max_pup = (1 - ecc) *
+					dram_info->num_of_std_pups + ecc;
+
+				if (ecc) {
+					/* Only 1 pup in this case */
+					valid_pup = 0x1;
+				} else if (cur_max_pup > 4) {
+					/* 64 bit - 8 pups */
+					valid_pup = 0xFF;
+				} else if (cur_max_pup == 4) {
+					/* 32 bit - 4 pups */
+					valid_pup = 0xF;
+				} else {
+					/* 16 bit - 2 pups */
+					valid_pup = 0x3;
+				}
+
+				/* ECC Support - Switch ECC Mux on ecc=1 */
+				reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+					~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg |= (dram_info->ecc_ena * ecc <<
+					REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+				if (ecc)
+					DEBUG_PBS_S("DDR3 - PBS Tx - ECC Mux Enabled\n");
+				else
+					DEBUG_PBS_S("DDR3 - PBS Tx - ECC Mux Disabled\n");
+
+				/* Init iteration values */
+				/* Clear the locked DQs */
+				for (pup = 0; pup < cur_max_pup; pup++) {
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						pbs_locked_dq[
+							pup + ecc *
+							(max_pup - 1)][dq] =
+							0;
+					}
+				}
+
+				pbs_rep_time = 0;
+				cur_pup = valid_pup;
+				start_over = 0;
+
+				/*
+				 * Run loop On current Pattern and current
+				 * pattern iteration (just to cover the false
+				 * fail problem)
+				 */
+				do {
+					DEBUG_PBS_S("DDR3 - PBS Tx - Pbs Rep Loop is ");
+					DEBUG_PBS_D(pbs_rep_time, 1);
+					DEBUG_PBS_S(", for Retry No.");
+					DEBUG_PBS_D(pbs_retry, 1);
+					DEBUG_PBS_S("\n");
+
+					/* Set all PBS values to MIN (0) */
+					DEBUG_PBS_S("DDR3 - PBS Tx - Set all PBS values to MIN\n");
+
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						ddr3_write_pup_reg(
+							PUP_PBS_TX +
+							pbs_dq_mapping[pup *
+								(1 - ecc) +
+								ecc * ECC_PUP]
+							[dq], CS0, (1 - ecc) *
+							PUP_BC + ecc * ECC_PUP, 0,
+							0);
+					}
+
+					/*
+					 * Shift DQ ADLL right, One step before
+					 * fail
+					 */
+					DEBUG_PBS_S("DDR3 - PBS Tx - ADLL shift right one phase before fail\n");
+
+					if (MV_OK != ddr3_tx_shift_dqs_adll_step_before_fail
+					    (dram_info, cur_pup, pattern_idx,
+					     ecc))
+						return MV_DDR3_TRAINING_ERR_PBS_ADLL_SHR_1PHASE;
+
+					/* PBS For each bit */
+					DEBUG_PBS_S("DDR3 - PBS Tx - perform PBS for each bit\n");
+
+					/*
+					 * In this stage - start_over = 0
+					 */
+					if (MV_OK != ddr3_pbs_per_bit(
+						    dram_info, &start_over, 1,
+						    &cur_pup, pattern_idx, ecc))
+						return MV_DDR3_TRAINING_ERR_PBS_TX_PER_BIT;
+
+				} while ((start_over == 1) &&
+					 (++pbs_rep_time < COUNT_PBS_STARTOVER));
+
+				if (pbs_rep_time == COUNT_PBS_STARTOVER &&
+				    start_over == 1) {
+					DEBUG_PBS_S("DDR3 - PBS Tx - FAIL - Adll reach max value\n");
+					return MV_DDR3_TRAINING_ERR_PBS_TX_MAX_VAL;
+				}
+
+				DEBUG_PBS_FULL_C("DDR3 - PBS TX - values for iteration - ",
+						 pbs_retry, 1);
+				for (pup = 0; pup < cur_max_pup; pup++) {
+					/*
+					 * To minimize delay elements, inc
+					 * from pbs value the min pbs val
+					 */
+					DEBUG_PBS_S("DDR3 - PBS - PUP");
+					DEBUG_PBS_D((pup + (ecc * ECC_PUP)), 1);
+					DEBUG_PBS_S(": ");
+
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						/* Set skew value for all dq */
+						/*
+						 * Bit# Deskew <- Bit# Deskew -
+						 * last / first  failing bit
+						 * Deskew For all bits (per PUP)
+						 * (minimize delay elements)
+						 */
+						DEBUG_PBS_S("DQ");
+						DEBUG_PBS_D(dq, 1);
+						DEBUG_PBS_S("-");
+						DEBUG_PBS_D(skew_array
+							    [((pup) * DQ_NUM) +
+							     dq], 2);
+						DEBUG_PBS_S(", ");
+					}
+					DEBUG_PBS_S("\n");
+				}
+
+				/*
+				 * Collect the results we got on this trial
+				 * of PBS
+				 */
+				for (pup = 0; pup < cur_max_pup; pup++) {
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						skew_sum_array[pup + (ecc * (max_pup - 1))]
+							[dq] += skew_array
+							[((pup) * DQ_NUM) + dq];
+					}
+				}
+
+				/* ECC Support - Disable ECC MUX */
+				reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+					~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+			}
+		}
+
+		DEBUG_PBS_C("DDR3 - PBS TX - values for current pattern - ",
+			    pattern_idx, 1);
+		for (pup = 0; pup < max_pup; pup++) {
+			/*
+			 * To minimize delay elements, inc from pbs value the
+			 * min pbs val
+			 */
+			DEBUG_PBS_S("DDR3 - PBS - PUP");
+			DEBUG_PBS_D(pup, 1);
+			DEBUG_PBS_S(": ");
+
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				/* set skew value for all dq */
+				/* Bit# Deskew <- Bit# Deskew - last / first  failing bit Deskew For all bits (per PUP) (minimize delay elements) */
+				DEBUG_PBS_S("DQ");
+				DEBUG_PBS_D(dq, 1);
+				DEBUG_PBS_S("-");
+				DEBUG_PBS_D(skew_sum_array[pup][dq] /
+					    COUNT_PBS_REPEAT, 2);
+				DEBUG_PBS_S(", ");
+			}
+			DEBUG_PBS_S("\n");
+		}
+
+		/*
+		 * Calculate the average skew for current pattern for each
+		 * pup and each bit
+		 */
+		DEBUG_PBS_C("DDR3 - PBS TX - Average for pattern - ",
+			    pattern_idx, 1);
+
+		for (pup = 0; pup < max_pup; pup++) {
+			/*
+			 * FOR ECC only :: found min and max value for current
+			 * pattern skew array
+			 */
+			/* Loop for all dqs */
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				pattern_skew_array[pup][dq] +=
+					(skew_sum_array[pup][dq] /
+					 COUNT_PBS_REPEAT);
+			}
+		}
+	}
+
+	/* Calculate the average skew */
+	for (pup = 0; pup < max_pup; pup++) {
+		for (dq = 0; dq < DQ_NUM; dq++)
+			skew_array[((pup) * DQ_NUM) + dq] =
+				pattern_skew_array[pup][dq] / COUNT_PBS_PATTERN;
+	}
+
+	DEBUG_PBS_S("DDR3 - PBS TX - Average for all patterns:\n");
+	for (pup = 0; pup < max_pup; pup++) {
+		/*
+		 * To minimize delay elements, inc from pbs value the min
+		 * pbs val
+		 */
+		DEBUG_PBS_S("DDR3 - PBS - PUP");
+		DEBUG_PBS_D(pup, 1);
+		DEBUG_PBS_S(": ");
+
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			/* Set skew value for all dq */
+			/*
+			 * Bit# Deskew <- Bit# Deskew - last / first
+			 * failing bit Deskew For all bits (per PUP)
+			 * (minimize delay elements)
+			 */
+			DEBUG_PBS_S("DQ");
+			DEBUG_PBS_D(dq, 1);
+			DEBUG_PBS_S("-");
+			DEBUG_PBS_D(skew_array[(pup * DQ_NUM) + dq], 2);
+			DEBUG_PBS_S(", ");
+		}
+		DEBUG_PBS_S("\n");
+	}
+
+	/* Return ADLL to default value */
+	for (pup = 0; pup < max_pup; pup++) {
+		if (pup == (max_pup - 1) && dram_info->ecc_ena)
+			pup = ECC_PUP;
+		ddr3_pbs_write_pup_dqs_reg(CS0, pup, INIT_WL_DELAY);
+	}
+
+	/* Set averaged PBS results */
+	ddr3_set_pbs_results(dram_info, 1);
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
+		(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
+	reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
+
+	DEBUG_PBS_S("DDR3 - PBS Tx - PBS TX ended successfuly\n");
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_tx_shift_dqs_adll_step_before_fail
+ * Desc:     Execute the Tx shift DQ phase.
+ * Args:     dram_info            ddr3 training information struct
+ *           cur_pup              bit array of the function active pups.
+ *           pbs_pattern_idx      Index of PBS pattern
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static int ddr3_tx_shift_dqs_adll_step_before_fail(MV_DRAM_INFO *dram_info,
+						   u32 cur_pup,
+						   u32 pbs_pattern_idx, u32 ecc)
+{
+	u32 unlock_pup;		/* bit array of unlock pups  */
+	u32 new_lockup_pup;	/* bit array of compare failed pups */
+	u32 adll_val = 4;	/* INIT_WL_DELAY */
+	u32 cur_max_pup, pup;
+	u32 dqs_dly_set[MAX_PUP_NUM] = { 0 };
+	u32 *pattern_ptr;
+
+	/* Choose pattern */
+	switch (dram_info->ddr_width) {
+#if defined(MV88F672X)
+	case 16:
+		pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx];
+		break;
+#endif
+	case 32:
+		pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx];
+		break;
+#if defined(MV88F78X60)
+	case 64:
+		pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx];
+		break;
+#endif
+	default:
+		return MV_FAIL;
+	}
+
+	/* Set current pup number */
+	if (cur_pup == 0x1)	/* Ecc mode */
+		cur_max_pup = 1;
+	else
+		cur_max_pup = dram_info->num_of_std_pups;
+
+	unlock_pup = cur_pup;	/* '1' for each unlocked pup */
+
+	/* Loop on all ADLL Vaules */
+	do {
+		/* Loop until found first fail */
+		adll_val++;
+
+		/*
+		 * Increment (Move to right - ADLL) DQ TX delay
+		 * (broadcast to all Data PUPs)
+		 */
+		for (pup = 0; pup < cur_max_pup; pup++)
+			ddr3_pbs_write_pup_dqs_reg(CS0,
+						   pup * (1 - ecc) +
+						   ECC_PUP * ecc, adll_val);
+
+		/*
+		 * Write and Read, compare results (read was already verified)
+		 */
+		/* 0 - all locked */
+		new_lockup_pup = 0;
+
+		if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup,
+						&new_lockup_pup,
+						pattern_ptr, LEN_PBS_PATTERN,
+						SDRAM_PBS_TX_OFFS, 1, 0,
+						NULL,
+						0))
+			return MV_FAIL;
+
+		unlock_pup &= ~new_lockup_pup;
+
+		DEBUG_PBS_FULL_S("Shift DQS by 2 steps for PUPs: ");
+		DEBUG_PBS_FULL_D(unlock_pup, 2);
+		DEBUG_PBS_FULL_C(", Set ADLL value = ", adll_val, 2);
+
+		/* If any PUP failed there is '1' to mark the PUP */
+		if (new_lockup_pup != 0) {
+			/*
+			 * Decrement (Move Back to Left two steps - ADLL)
+			 * DQ TX delay for current failed pups and save
+			 */
+			for (pup = 0; pup < cur_max_pup; pup++) {
+				if (((new_lockup_pup >> pup) & 0x1) &&
+				    dqs_dly_set[pup] == 0)
+					dqs_dly_set[pup] = adll_val - 1;
+			}
+		}
+	} while ((unlock_pup != 0) && (adll_val != ADLL_MAX));
+
+	if (unlock_pup != 0) {
+		DEBUG_PBS_FULL_S("DDR3 - PBS Tx - Shift DQ - Adll value reached maximum\n");
+
+		for (pup = 0; pup < cur_max_pup; pup++) {
+			if (((unlock_pup >> pup) & 0x1) &&
+			    dqs_dly_set[pup] == 0)
+				dqs_dly_set[pup] = adll_val - 1;
+		}
+	}
+
+	DEBUG_PBS_FULL_C("PBS TX one step before fail last pups locked Adll ",
+			 adll_val - 2, 2);
+
+	/* Set the PUP DQS DLY Values */
+	for (pup = 0; pup < cur_max_pup; pup++)
+		ddr3_pbs_write_pup_dqs_reg(CS0, pup * (1 - ecc) + ECC_PUP * ecc,
+					   dqs_dly_set[pup]);
+
+	/* Found one phase before fail */
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_pbs_rx
+ * Desc:     Execute the PBS RX phase.
+ * Args:     dram_info   ddr3 training information struct
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_pbs_rx(MV_DRAM_INFO *dram_info)
+{
+	/*
+	 * Array to hold the total sum of skew from all iterations
+	 * (for average purpose)
+	 */
+	u32 skew_sum_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
+
+	/*
+	 * Array to hold the total average skew from both patterns
+	 * (for average purpose)
+	 */
+	u32 pattern_skew_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
+
+	u32 pbs_rep_time = 0;	/* counts number of loop in case of fail */
+	/* bit array for unlock pups - used to repeat on the RX operation */
+	u32 cur_pup;
+	u32 max_pup;
+	u32 pbs_retry;
+	u32 pup, dq, pups, cur_max_pup, valid_pup, reg;
+	u32 pattern_idx;
+	u32 ecc;
+	/* indicates whether we need to start the loop again */
+	int start_over;
+	int status;
+
+	DEBUG_PBS_S("DDR3 - PBS RX - Starting PBS RX procedure\n");
+
+	pups = dram_info->num_of_total_pups;
+	max_pup = dram_info->num_of_total_pups;
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_PBS_FULL_S("DDR3 - PBS RX - SW Override Enabled\n");
+
+	reg = 1 << REG_DRAM_TRAINING_AUTO_OFFS;
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	/* Running twice for 2 different patterns. each patterns - 3 times */
+	for (pattern_idx = 0; pattern_idx < COUNT_PBS_PATTERN; pattern_idx++) {
+		DEBUG_PBS_FULL_C("DDR3 - PBS RX - Working with pattern - ",
+				 pattern_idx, 1);
+
+		/* Reset sum array */
+		for (pup = 0; pup < pups; pup++) {
+			for (dq = 0; dq < DQ_NUM; dq++)
+				skew_sum_array[pup][dq] = 0;
+		}
+
+		/*
+		 * Perform PBS several of times (3 for each pattern).
+		 * At the end, we'll use the average
+		 */
+		/* If there is ECC, do each PBS again with mux change */
+		for (pbs_retry = 0; pbs_retry < COUNT_PBS_REPEAT; pbs_retry++) {
+			for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) {
+				/*
+				 * This parameter stores the current PUP
+				 * num - ecc mode dependent - 4-8 / 1 pups
+				 */
+				cur_max_pup = (1 - ecc) *
+					dram_info->num_of_std_pups + ecc;
+
+				if (ecc) {
+					/* Only 1 pup in this case */
+					valid_pup = 0x1;
+				} else if (cur_max_pup > 4) {
+					/* 64 bit - 8 pups */
+					valid_pup = 0xFF;
+				} else if (cur_max_pup == 4) {
+					/* 32 bit - 4 pups */
+					valid_pup = 0xF;
+				} else {
+					/* 16 bit - 2 pups */
+					valid_pup = 0x3;
+				}
+
+				/* ECC Support - Switch ECC Mux on ecc=1 */
+				reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+					~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg |= (dram_info->ecc_ena * ecc <<
+					REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+				if (ecc)
+					DEBUG_PBS_FULL_S("DDR3 - PBS Rx - ECC Mux Enabled\n");
+				else
+					DEBUG_PBS_FULL_S("DDR3 - PBS Rx - ECC Mux Disabled\n");
+
+				/* Init iteration values */
+				/* Clear the locked DQs */
+				for (pup = 0; pup < cur_max_pup; pup++) {
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						pbs_locked_dq[
+							pup + ecc * (max_pup - 1)][dq] =
+							0;
+					}
+				}
+
+				pbs_rep_time = 0;
+				cur_pup = valid_pup;
+				start_over = 0;
+
+				/*
+				 * Run loop On current Pattern and current
+				 * pattern iteration (just to cover the false
+				 * fail problem
+				 */
+				do {
+					DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Pbs Rep Loop is ");
+					DEBUG_PBS_FULL_D(pbs_rep_time, 1);
+					DEBUG_PBS_FULL_S(", for Retry No.");
+					DEBUG_PBS_FULL_D(pbs_retry, 1);
+					DEBUG_PBS_FULL_S("\n");
+
+					/* Set all PBS values to MAX (31) */
+					for (pup = 0; pup < cur_max_pup; pup++) {
+						for (dq = 0; dq < DQ_NUM; dq++)
+							ddr3_write_pup_reg(
+								PUP_PBS_RX +
+								pbs_dq_mapping[
+								pup * (1 - ecc)
+								+ ecc * ECC_PUP]
+								[dq], CS0,
+								pup + ecc * ECC_PUP,
+								0, MAX_PBS);
+					}
+
+					/* Set all DQS PBS values to MIN (0) */
+					for (pup = 0; pup < cur_max_pup; pup++) {
+						ddr3_write_pup_reg(PUP_PBS_RX +
+								   DQ_NUM, CS0,
+								   pup +
+								   ecc *
+								   ECC_PUP, 0,
+								   0);
+					}
+
+					/* Shift DQS, To first Fail */
+					DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Shift RX DQS to first fail\n");
+
+					status = ddr3_rx_shift_dqs_to_first_fail
+						(dram_info, cur_pup,
+						 pattern_idx, ecc);
+					if (MV_OK != status) {
+						DEBUG_PBS_S("DDR3 - PBS Rx - ddr3_rx_shift_dqs_to_first_fail failed.\n");
+						DEBUG_PBS_D(status, 8);
+						DEBUG_PBS_S("\nDDR3 - PBS Rx - SKIP.\n");
+
+						/* Reset read FIFO */
+						reg = reg_read(REG_DRAM_TRAINING_ADDR);
+						/* Start Auto Read Leveling procedure */
+						reg |= (1 << REG_DRAM_TRAINING_RL_OFFS);
+						/* 0x15B0 - Training Register */
+						reg_write(REG_DRAM_TRAINING_ADDR, reg);
+
+						reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+						reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS)
+							+ (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS));
+						/* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset  */
+						/* 0x15B8 - Training SW 2 Register */
+						reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+						do {
+							reg = (reg_read(REG_DRAM_TRAINING_2_ADDR))
+								& (1 <<	REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+						} while (reg);	/* Wait for '0' */
+
+						reg = reg_read(REG_DRAM_TRAINING_ADDR);
+						/* Clear Auto Read Leveling procedure */
+						reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS);
+						/* 0x15B0 - Training Register */
+						reg_write(REG_DRAM_TRAINING_ADDR, reg);
+
+						/* Set ADLL to 15 */
+						for (pup = 0; pup < max_pup;
+						     pup++) {
+							ddr3_write_pup_reg
+							    (PUP_DQS_RD, CS0,
+							     pup +
+							     (ecc * ECC_PUP), 0,
+							     15);
+						}
+
+						/* Set all PBS values to MIN (0) */
+						for (pup = 0; pup < cur_max_pup;
+						     pup++) {
+							for (dq = 0;
+							     dq < DQ_NUM; dq++)
+								ddr3_write_pup_reg
+								    (PUP_PBS_RX +
+								     pbs_dq_mapping
+								     [pup * (1 - ecc) +
+								      ecc * ECC_PUP]
+								     [dq], CS0,
+								     pup + ecc * ECC_PUP,
+								     0, MIN_PBS);
+						}
+
+						return MV_OK;
+					}
+
+					/* PBS For each bit */
+					DEBUG_PBS_FULL_S("DDR3 - PBS Rx - perform PBS for each bit\n");
+					/* in this stage - start_over = 0; */
+					if (MV_OK != ddr3_pbs_per_bit(
+						    dram_info, &start_over,
+						    0, &cur_pup,
+						    pattern_idx, ecc)) {
+						DEBUG_PBS_S("DDR3 - PBS Rx - ddr3_pbs_per_bit failed.");
+						return MV_DDR3_TRAINING_ERR_PBS_RX_PER_BIT;
+					}
+
+				} while ((start_over == 1) &&
+					 (++pbs_rep_time < COUNT_PBS_STARTOVER));
+
+				if (pbs_rep_time == COUNT_PBS_STARTOVER &&
+				    start_over == 1) {
+					DEBUG_PBS_FULL_S("DDR3 - PBS Rx - FAIL - Algorithm failed doing RX PBS\n");
+					return MV_DDR3_TRAINING_ERR_PBS_RX_MAX_VAL;
+				}
+
+				/* Return DQS ADLL to default value - 15 */
+				/* Set all DQS PBS values to MIN (0) */
+				for (pup = 0; pup < cur_max_pup; pup++)
+					ddr3_write_pup_reg(PUP_DQS_RD, CS0,
+							   pup + ecc * ECC_PUP,
+							   0, INIT_RL_DELAY);
+
+				DEBUG_PBS_FULL_C("DDR3 - PBS RX - values for iteration - ",
+						 pbs_retry, 1);
+				for (pup = 0; pup < cur_max_pup; pup++) {
+					/*
+					 * To minimize delay elements, inc from
+					 * pbs value the min pbs val
+					 */
+					DEBUG_PBS_FULL_S("DDR3 - PBS - PUP");
+					DEBUG_PBS_FULL_D((pup +
+							  (ecc * ECC_PUP)), 1);
+					DEBUG_PBS_FULL_S(": ");
+
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						/* Set skew value for all dq */
+						/*
+						 * Bit# Deskew <- Bit# Deskew -
+						 * last / first  failing bit
+						 * Deskew For all bits (per PUP)
+						 * (minimize delay elements)
+						 */
+						DEBUG_PBS_FULL_S("DQ");
+						DEBUG_PBS_FULL_D(dq, 1);
+						DEBUG_PBS_FULL_S("-");
+						DEBUG_PBS_FULL_D(skew_array
+								 [((pup) *
+								   DQ_NUM) +
+								  dq], 2);
+						DEBUG_PBS_FULL_S(", ");
+					}
+					DEBUG_PBS_FULL_S("\n");
+				}
+
+				/*
+				 * Collect the results we got on this trial
+				 * of PBS
+				 */
+				for (pup = 0; pup < cur_max_pup; pup++) {
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						skew_sum_array
+							[pup + (ecc * (max_pup - 1))]
+							[dq] +=
+							skew_array[((pup) * DQ_NUM) + dq];
+					}
+				}
+
+				/* ECC Support - Disable ECC MUX */
+				reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+					~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+				reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+			}
+		}
+
+		/*
+		 * Calculate the average skew for current pattern for each
+		 * pup and each bit
+		 */
+		DEBUG_PBS_FULL_C("DDR3 - PBS RX - Average for pattern - ",
+				 pattern_idx, 1);
+		for (pup = 0; pup < max_pup; pup++) {
+			/*
+			 * FOR ECC only :: found min and max value for
+			 * current pattern skew array
+			 */
+			/* Loop for all dqs */
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				pattern_skew_array[pup][dq] +=
+					(skew_sum_array[pup][dq] /
+					 COUNT_PBS_REPEAT);
+			}
+		}
+
+		DEBUG_PBS_C("DDR3 - PBS RX - values for current pattern - ",
+			    pattern_idx, 1);
+		for (pup = 0; pup < max_pup; pup++) {
+			/*
+			 * To minimize delay elements, inc from pbs value the
+			 * min pbs val
+			 */
+			DEBUG_PBS_S("DDR3 - PBS RX - PUP");
+			DEBUG_PBS_D(pup, 1);
+			DEBUG_PBS_S(": ");
+
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				/* Set skew value for all dq */
+				/*
+				 * Bit# Deskew <- Bit# Deskew - last / first
+				 * failing bit Deskew For all bits (per PUP)
+				 * (minimize delay elements)
+				 */
+				DEBUG_PBS_S("DQ");
+				DEBUG_PBS_D(dq, 1);
+				DEBUG_PBS_S("-");
+				DEBUG_PBS_D(skew_sum_array[pup][dq] /
+					    COUNT_PBS_REPEAT, 2);
+				DEBUG_PBS_S(", ");
+			}
+			DEBUG_PBS_S("\n");
+		}
+	}
+
+	/* Calculate the average skew */
+	for (pup = 0; pup < max_pup; pup++) {
+		for (dq = 0; dq < DQ_NUM; dq++)
+			skew_array[((pup) * DQ_NUM) + dq] =
+				pattern_skew_array[pup][dq] / COUNT_PBS_PATTERN;
+	}
+
+	DEBUG_PBS_S("DDR3 - PBS RX - Average for all patterns:\n");
+	for (pup = 0; pup < max_pup; pup++) {
+		/*
+		 * To minimize delay elements, inc from pbs value the
+		 * min pbs val
+		 */
+		DEBUG_PBS_S("DDR3 - PBS - PUP");
+		DEBUG_PBS_D(pup, 1);
+		DEBUG_PBS_S(": ");
+
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			/* Set skew value for all dq */
+			/*
+			 * Bit# Deskew <- Bit# Deskew - last / first
+			 * failing bit Deskew For all bits (per PUP)
+			 * (minimize delay elements)
+			 */
+			DEBUG_PBS_S("DQ");
+			DEBUG_PBS_D(dq, 1);
+			DEBUG_PBS_S("-");
+			DEBUG_PBS_D(skew_array[(pup * DQ_NUM) + dq], 2);
+			DEBUG_PBS_S(", ");
+		}
+		DEBUG_PBS_S("\n");
+	}
+
+	/* Return ADLL to default value */
+	ddr3_write_pup_reg(PUP_DQS_RD, CS0, PUP_BC, 0, INIT_RL_DELAY);
+
+	/* Set averaged PBS results */
+	ddr3_set_pbs_results(dram_info, 0);
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
+		(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
+	reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
+
+	DEBUG_PBS_FULL_S("DDR3 - PBS RX - ended successfuly\n");
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_rx_shift_dqs_to_first_fail
+ * Desc:     Execute the Rx shift DQ phase.
+ * Args:     dram_info           ddr3 training information struct
+ *           cur_pup             bit array of the function active pups.
+ *           pbs_pattern_idx     Index of PBS pattern
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static int ddr3_rx_shift_dqs_to_first_fail(MV_DRAM_INFO *dram_info, u32 cur_pup,
+					   u32 pbs_pattern_idx, u32 ecc)
+{
+	u32 unlock_pup;		/* bit array of unlock pups  */
+	u32 new_lockup_pup;	/* bit array of compare failed pups */
+	u32 adll_val = MAX_DELAY;
+	u32 dqs_deskew_val = 0;	/* current value of DQS PBS deskew */
+	u32 cur_max_pup, pup, pass_pup;
+	u32 *pattern_ptr;
+
+	/* Choose pattern */
+	switch (dram_info->ddr_width) {
+#if defined(MV88F672X)
+	case 16:
+		pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx];
+		break;
+#endif
+	case 32:
+		pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx];
+		break;
+#if defined(MV88F78X60)
+	case 64:
+		pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx];
+		break;
+#endif
+	default:
+		return MV_FAIL;
+	}
+
+	/* Set current pup number */
+	if (cur_pup == 0x1)	/* Ecc mode */
+		cur_max_pup = 1;
+	else
+		cur_max_pup = dram_info->num_of_std_pups;
+
+	unlock_pup = cur_pup;	/* '1' for each unlocked pup */
+
+	DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Starting...\n");
+
+	/* Set DQS ADLL to MAX */
+	DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Set DQS ADLL to Max for all PUPs\n");
+	for (pup = 0; pup < cur_max_pup; pup++)
+		ddr3_write_pup_reg(PUP_DQS_RD, CS0, pup + ecc * ECC_PUP, 0,
+				   MAX_DELAY);
+
+	/* Loop on all ADLL Vaules */
+	do {
+		/* Loop until found fail for all pups */
+		new_lockup_pup = 0;
+		if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup,
+						&new_lockup_pup,
+						pattern_ptr, LEN_PBS_PATTERN,
+						SDRAM_PBS_I_OFFS +
+						pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS,
+						0, 0, NULL, 0)) {
+			DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP(ddr3_sdram_compare)\n");
+			return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP;
+		}
+
+		if ((new_lockup_pup != 0) && (dqs_deskew_val <= 1)) {
+			/* Fail on start with first deskew value */
+			/* Decrement DQS ADLL */
+			--adll_val;
+			if (adll_val == ADLL_MIN) {
+				DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - fail on start with first deskew value\n");
+				return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP;
+			}
+			ddr3_write_pup_reg(PUP_DQS_RD, CS0, pup + ecc * ECC_PUP,
+					   0, adll_val);
+			continue;
+		}
+
+		/* Update all new locked pups */
+		unlock_pup &= ~new_lockup_pup;
+
+		if ((unlock_pup == 0) || (dqs_deskew_val == MAX_PBS)) {
+			if (dqs_deskew_val == MAX_PBS) {
+				/*
+				 * Reach max value of dqs deskew or get fail
+				 * for all pups
+				 */
+				DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - DQS deskew reached maximum value\n");
+			}
+			break;
+		}
+
+		DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Inc DQS deskew for PUPs: ");
+		DEBUG_PBS_FULL_D(unlock_pup, 2);
+		DEBUG_PBS_FULL_C(", deskew = ", dqs_deskew_val, 2);
+
+		/* Increment DQS deskew elements - Only for unlocked pups */
+		dqs_deskew_val++;
+		for (pup = 0; pup < cur_max_pup; pup++) {
+			if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
+				ddr3_write_pup_reg(PUP_PBS_RX + DQS_DQ_NUM, CS0,
+						   pup + ecc * ECC_PUP, 0,
+						   dqs_deskew_val);
+			}
+		}
+	} while (1);
+
+	DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - ADLL shift one step before fail\n");
+	/* Continue to ADLL shift one step before fail */
+	unlock_pup = cur_pup;
+	do {
+		/* Loop until pass compare for all pups */
+		new_lockup_pup = 0;
+		/* Read and compare results  */
+		if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup, &new_lockup_pup,
+						pattern_ptr, LEN_PBS_PATTERN,
+						SDRAM_PBS_I_OFFS +
+						pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS,
+						1, 0, NULL, 0)) {
+			DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP(ddr3_sdram_compare)\n");
+			return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP;
+		}
+
+		/*
+		 * Get mask for pup which passed so their adll will be
+		 * changed to 2 steps before fails
+		 */
+		pass_pup = unlock_pup & ~new_lockup_pup;
+
+		DEBUG_PBS_FULL_S("Shift DQS by 2 steps for PUPs: ");
+		DEBUG_PBS_FULL_D(pass_pup, 2);
+		DEBUG_PBS_FULL_C(", Set ADLL value = ", (adll_val - 2), 2);
+
+		/* Only for pass pups   */
+		for (pup = 0; pup < cur_max_pup; pup++) {
+			if (IS_PUP_ACTIVE(pass_pup, pup) == 1) {
+				ddr3_write_pup_reg(PUP_DQS_RD, CS0,
+						   pup + ecc * ECC_PUP, 0,
+						   (adll_val - 2));
+			}
+		}
+
+		/* Locked pups that compare success  */
+		unlock_pup &= new_lockup_pup;
+
+		if (unlock_pup == 0) {
+			/* All pups locked */
+			break;
+		}
+
+		/* Found error */
+		if (adll_val == 0) {
+			DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Shift DQS - Adll reach min value\n");
+			return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_MAX_VAL;
+		}
+
+		/*
+		 * Decrement (Move Back to Left one phase - ADLL) dqs RX delay
+		 */
+		adll_val--;
+		for (pup = 0; pup < cur_max_pup; pup++) {
+			if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
+				ddr3_write_pup_reg(PUP_DQS_RD, CS0,
+						   pup + ecc * ECC_PUP, 0,
+						   adll_val);
+			}
+		}
+	} while (1);
+
+	return MV_OK;
+}
+
+/*
+ * lock_pups() extracted from ddr3_pbs_per_bit(). This just got too
+ * much indented making it hard to read / edit.
+ */
+static void lock_pups(u32 pup, u32 *pup_locked, u8 *unlock_pup_dq_array,
+		      u32 pbs_curr_val, u32 start_pbs, u32 ecc, int is_tx)
+{
+	u32 dq;
+	int idx;
+
+	/* Lock PBS value for all remaining PUPs bits */
+	DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Lock PBS value for all remaining PUPs bits, pup ");
+	DEBUG_PBS_FULL_D(pup, 1);
+	DEBUG_PBS_FULL_C(" pbs value ", pbs_curr_val, 2);
+
+	idx = pup * (1 - ecc) + ecc * ECC_PUP;
+	*pup_locked &= ~(1 << pup);
+
+	for (dq = 0; dq < DQ_NUM; dq++) {
+		if (IS_PUP_ACTIVE(unlock_pup_dq_array[dq], pup) == 1) {
+			int offs;
+
+			/* Lock current dq */
+			unlock_pup_dq_array[dq] &= ~(1 << pup);
+			skew_array[(pup * DQ_NUM) + dq] = pbs_curr_val;
+
+			if (is_tx == 1)
+				offs = PUP_PBS_TX;
+			else
+				offs = PUP_PBS_RX;
+
+			ddr3_write_pup_reg(offs +
+					   pbs_dq_mapping[idx][dq], CS0,
+					   idx, 0, start_pbs);
+		}
+	}
+}
+
+/*
+ * Name:     ddr3_pbs_per_bit
+ * Desc:     Execute the Per Bit Skew phase.
+ * Args:     start_over      Return whether need to start over the algorithm
+ *           is_tx           Indicate whether Rx or Tx
+ *           pcur_pup        bit array of the function active pups. return the
+ *                           pups that need to repeat on the PBS
+ *           pbs_pattern_idx Index of PBS pattern
+ *
+ * Notes:    Current implementation supports double activation of this function.
+ *           i.e. in order to activate this function (using start_over) more than
+ *           twice, the implementation should change.
+ *           imlementation limitation are marked using
+ *           ' CHIP-ONLY! - Implementation Limitation '
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static int ddr3_pbs_per_bit(MV_DRAM_INFO *dram_info, int *start_over, int is_tx,
+			    u32 *pcur_pup, u32 pbs_pattern_idx, u32 ecc)
+{
+	/*
+	 * Bit array to indicate if we already get fail on bit per pup & dq bit
+	 */
+	u8 unlock_pup_dq_array[DQ_NUM] = {
+		*pcur_pup, *pcur_pup, *pcur_pup, *pcur_pup, *pcur_pup,
+		*pcur_pup, *pcur_pup, *pcur_pup
+	};
+
+	u8 cmp_unlock_pup_dq_array[COUNT_PBS_COMP_RETRY_NUM][DQ_NUM];
+	u32 pup, dq;
+	/* value of pbs is according to RX or TX */
+	u32 start_pbs, last_pbs;
+	u32 pbs_curr_val;
+	/* bit array that indicates all dq of the pup locked */
+	u32 pup_locked;
+	u32 first_fail[MAX_PUP_NUM] = { 0 };	/* count first fail per pup */
+	/* indicates whether we get first fail per pup */
+	int first_failed[MAX_PUP_NUM] = { 0 };
+	/* bit array that indicates pup already get fail */
+	u32 sum_pup_fail;
+	/* use to calculate diff between curr pbs to first fail pbs */
+	u32 calc_pbs_diff;
+	u32 pbs_cmp_retry;
+	u32 max_pup;
+
+	/* Set init values for retry array - 8 retry */
+	for (pbs_cmp_retry = 0; pbs_cmp_retry < COUNT_PBS_COMP_RETRY_NUM;
+	     pbs_cmp_retry++) {
+		for (dq = 0; dq < DQ_NUM; dq++)
+			cmp_unlock_pup_dq_array[pbs_cmp_retry][dq] = *pcur_pup;
+	}
+
+	memset(&skew_array, 0, MAX_PUP_NUM * DQ_NUM * sizeof(u32));
+
+	DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Started\n");
+
+	/* The pbs value depends if rx or tx */
+	if (is_tx == 1) {
+		start_pbs = MIN_PBS;
+		last_pbs = MAX_PBS;
+	} else {
+		start_pbs = MAX_PBS;
+		last_pbs = MIN_PBS;
+	}
+
+	pbs_curr_val = start_pbs;
+	pup_locked = *pcur_pup;
+
+	/* Set current pup number */
+	if (pup_locked == 0x1)	/* Ecc mode */
+		max_pup = 1;
+	else
+		max_pup = dram_info->num_of_std_pups;
+
+	do {
+		/* Increment/ decrement PBS for un-lock bits only */
+		if (is_tx == 1)
+			pbs_curr_val++;
+		else
+			pbs_curr_val--;
+
+		/* Set Current PBS delay  */
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			/* Check DQ bits to see if locked in all pups */
+			if (unlock_pup_dq_array[dq] == 0) {
+				DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - All pups are locked for DQ ");
+				DEBUG_PBS_FULL_D(dq, 1);
+				DEBUG_PBS_FULL_S("\n");
+				continue;
+			}
+
+			for (pup = 0; pup < max_pup; pup++) {
+				int idx;
+
+				idx = pup * (1 - ecc) + ecc * ECC_PUP;
+
+				if (IS_PUP_ACTIVE(unlock_pup_dq_array[dq], pup)
+				    == 0)
+					continue;
+
+				if (is_tx == 1)
+					ddr3_write_pup_reg(
+						PUP_PBS_TX + pbs_dq_mapping[idx][dq],
+						CS0, idx, 0, pbs_curr_val);
+				else
+					ddr3_write_pup_reg(
+						PUP_PBS_RX + pbs_dq_mapping[idx][dq],
+						CS0, idx, 0, pbs_curr_val);
+			}
+		}
+
+		/*
+		 * Write Read and compare results - run the test
+		 * DDR_PBS_COMP_RETRY_NUM times
+		 */
+		/* Run number of read and write to verify */
+		for (pbs_cmp_retry = 0;
+		     pbs_cmp_retry < COUNT_PBS_COMP_RETRY_NUM;
+		     pbs_cmp_retry++) {
+
+			if (MV_OK !=
+			    ddr3_sdram_pbs_compare(dram_info, pup_locked, is_tx,
+						   pbs_pattern_idx,
+						   pbs_curr_val, start_pbs,
+						   skew_array,
+						   cmp_unlock_pup_dq_array
+						   [pbs_cmp_retry], ecc))
+				return MV_FAIL;
+
+			for (pup = 0; pup < max_pup; pup++) {
+				for (dq = 0; dq < DQ_NUM; dq++) {
+					if ((IS_PUP_ACTIVE(unlock_pup_dq_array[dq],
+							   pup) == 1)
+					    && (IS_PUP_ACTIVE(cmp_unlock_pup_dq_array
+					      [pbs_cmp_retry][dq],
+					      pup) == 0)) {
+						DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - PbsCurrVal: ");
+						DEBUG_PBS_FULL_D(pbs_curr_val, 2);
+						DEBUG_PBS_FULL_S(" PUP: ");
+						DEBUG_PBS_FULL_D(pup, 1);
+						DEBUG_PBS_FULL_S(" DQ: ");
+						DEBUG_PBS_FULL_D(dq, 1);
+						DEBUG_PBS_FULL_S(" - failed\n");
+					}
+				}
+			}
+
+			for (dq = 0; dq < DQ_NUM; dq++) {
+				unlock_pup_dq_array[dq] &=
+				    cmp_unlock_pup_dq_array[pbs_cmp_retry][dq];
+			}
+		}
+
+		pup_locked = 0;
+		sum_pup_fail = *pcur_pup;
+
+		/* Check which DQ is failed */
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			/* Summarize the locked pup */
+			pup_locked |= unlock_pup_dq_array[dq];
+
+			/* Check if get fail */
+			sum_pup_fail &= unlock_pup_dq_array[dq];
+		}
+
+		/* If all PUPS are locked in all DQ - Break */
+		if (pup_locked == 0) {
+			/* All pups are locked */
+			*start_over = 0;
+			DEBUG_PBS_FULL_S("DDR3 - PBS Per bit -  All bit in all pups are successfully locked\n");
+			break;
+		}
+
+		/* PBS deskew elements reach max ? */
+		if (pbs_curr_val == last_pbs) {
+			DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - PBS deskew elements reach max\n");
+			/* CHIP-ONLY! - Implementation Limitation */
+			*start_over = (sum_pup_fail != 0) && (!(*start_over));
+			*pcur_pup = pup_locked;
+
+			DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - StartOver: ");
+			DEBUG_PBS_FULL_D(*start_over, 1);
+			DEBUG_PBS_FULL_S("  pup_locked: ");
+			DEBUG_PBS_FULL_D(pup_locked, 2);
+			DEBUG_PBS_FULL_S("  sum_pup_fail: ");
+			DEBUG_PBS_FULL_D(sum_pup_fail, 2);
+			DEBUG_PBS_FULL_S("\n");
+
+			/* Lock PBS value for all remaining  bits */
+			for (pup = 0; pup < max_pup; pup++) {
+				/* Check if current pup already received error */
+				if (IS_PUP_ACTIVE(pup_locked, pup) == 1) {
+					/* Valid pup for current function */
+					if (IS_PUP_ACTIVE(sum_pup_fail, pup) ==
+					    1 && (*start_over == 1)) {
+						DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - skipping lock of pup (first loop of pbs)",
+								 pup, 1);
+						continue;
+					} else
+					    if (IS_PUP_ACTIVE(sum_pup_fail, pup)
+						== 1) {
+						DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - Locking pup %d (even though it wasn't supposed to be locked)",
+								 pup, 1);
+					}
+
+					/* Already got fail on the PUP */
+					/* Lock PBS value for all remaining bits */
+					DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Locking remaning DQs for pup - ");
+					DEBUG_PBS_FULL_D(pup, 1);
+					DEBUG_PBS_FULL_S(": ");
+
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						if (IS_PUP_ACTIVE
+						    (unlock_pup_dq_array[dq],
+						     pup) == 1) {
+							DEBUG_PBS_FULL_D(dq, 1);
+							DEBUG_PBS_FULL_S(",");
+							/* set current PBS */
+							skew_array[((pup) *
+								    DQ_NUM) +
+								   dq] =
+							    pbs_curr_val;
+						}
+					}
+
+					if (*start_over == 1) {
+						/*
+						 * Reset this pup bit - when
+						 * restart the PBS, ignore this
+						 * pup
+						 */
+						*pcur_pup &= ~(1 << pup);
+					}
+					DEBUG_PBS_FULL_S("\n");
+				} else {
+					DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Pup ");
+					DEBUG_PBS_FULL_D(pup, 1);
+					DEBUG_PBS_FULL_C(" is not set in puplocked - ",
+							 pup_locked, 1);
+				}
+			}
+
+			/* Need to start the PBS again */
+			if (*start_over == 1) {
+				DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - false fail - returning to start\n");
+				return MV_OK;
+			}
+			break;
+		}
+
+		/* Diff Check */
+		for (pup = 0; pup < max_pup; pup++) {
+			if (IS_PUP_ACTIVE(pup_locked, pup) == 1) {
+				/* pup is not locked */
+				if (first_failed[pup] == 0) {
+					/* No first fail until now */
+					if (IS_PUP_ACTIVE(sum_pup_fail, pup) ==
+					    0) {
+						/* Get first fail */
+						DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - First fail in pup ",
+								 pup, 1);
+						first_failed[pup] = 1;
+						first_fail[pup] = pbs_curr_val;
+					}
+				} else {
+					/* Already got first fail */
+					if (is_tx == 1) {
+						/* TX - inc pbs */
+						calc_pbs_diff =	pbs_curr_val -
+							first_fail[pup];
+					} else {
+						/* RX - dec pbs */
+						calc_pbs_diff = first_fail[pup] -
+							pbs_curr_val;
+					}
+
+					if (calc_pbs_diff >= PBS_DIFF_LIMIT) {
+						lock_pups(pup, &pup_locked,
+							  unlock_pup_dq_array,
+							  pbs_curr_val,
+							  start_pbs, ecc, is_tx);
+					}
+				}
+			}
+		}
+	} while (1);
+
+	return MV_OK;
+}
+
+/*
+ * Name:         ddr3_set_pbs_results
+ * Desc:         Set to HW the PBS phase results.
+ * Args:         is_tx       Indicates whether to set Tx or RX results
+ * Notes:
+ * Returns:      MV_OK if success, other error code if fail.
+ */
+static int ddr3_set_pbs_results(MV_DRAM_INFO *dram_info, int is_tx)
+{
+	u32 pup, phys_pup, dq;
+	u32 max_pup;		/* number of valid pups */
+	u32 pbs_min;		/* minimal pbs val per pup */
+	u32 pbs_max;		/* maximum pbs val per pup */
+	u32 val[9];
+
+	max_pup = dram_info->num_of_total_pups;
+	DEBUG_PBS_FULL_S("DDR3 - PBS - ddr3_set_pbs_results:\n");
+
+	/* Loop for all dqs & pups */
+	for (pup = 0; pup < max_pup; pup++) {
+		if (pup == (max_pup - 1) && dram_info->ecc_ena)
+			phys_pup = ECC_PUP;
+		else
+			phys_pup = pup;
+
+		/*
+		 * To minimize delay elements, inc from pbs value the min
+		 * pbs val
+		 */
+		pbs_min = MAX_PBS;
+		pbs_max = 0;
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			if (pbs_min > skew_array[(pup * DQ_NUM) + dq])
+				pbs_min = skew_array[(pup * DQ_NUM) + dq];
+
+			if (pbs_max < skew_array[(pup * DQ_NUM) + dq])
+				pbs_max = skew_array[(pup * DQ_NUM) + dq];
+		}
+
+		pbs_max -= pbs_min;
+
+		DEBUG_PBS_FULL_S("DDR3 - PBS - PUP");
+		DEBUG_PBS_FULL_D(phys_pup, 1);
+		DEBUG_PBS_FULL_S(": Min Val = ");
+		DEBUG_PBS_FULL_D(pbs_min, 2);
+		DEBUG_PBS_FULL_C(", Max Val = ", pbs_max, 2);
+
+		val[pup] = 0;
+
+		for (dq = 0; dq < DQ_NUM; dq++) {
+			int idx;
+			int offs;
+
+			/* Set skew value for all dq */
+			/*
+			 * Bit# Deskew <- Bit# Deskew - last / first
+			 * failing bit Deskew For all bits (per PUP)
+			 * (minimize delay elements)
+			 */
+
+			DEBUG_PBS_FULL_S("DQ");
+			DEBUG_PBS_FULL_D(dq, 1);
+			DEBUG_PBS_FULL_S("-");
+			DEBUG_PBS_FULL_D((skew_array[(pup * DQ_NUM) + dq] -
+					  pbs_min), 2);
+			DEBUG_PBS_FULL_S(", ");
+
+			idx = (pup * DQ_NUM) + dq;
+
+			if (is_tx == 1)
+				offs = PUP_PBS_TX;
+			else
+				offs = PUP_PBS_RX;
+
+			ddr3_write_pup_reg(offs + pbs_dq_mapping[phys_pup][dq],
+					   CS0, phys_pup, 0,
+					   skew_array[idx] - pbs_min);
+
+			if (is_tx == 1)
+				val[pup] += skew_array[idx] - pbs_min;
+		}
+
+		DEBUG_PBS_FULL_S("\n");
+
+		/* Set the DQS the half of the Max PBS of the DQs  */
+		if (is_tx == 1) {
+			ddr3_write_pup_reg(PUP_PBS_TX + 8, CS0, phys_pup, 0,
+					   pbs_max / 2);
+			ddr3_write_pup_reg(PUP_PBS_TX + 0xa, CS0, phys_pup, 0,
+					   val[pup] / 8);
+		} else
+			ddr3_write_pup_reg(PUP_PBS_RX + 8, CS0, phys_pup, 0,
+					   pbs_max / 2);
+	}
+
+	return MV_OK;
+}
+
+static void ddr3_pbs_write_pup_dqs_reg(u32 cs, u32 pup, u32 dqs_delay)
+{
+	u32 reg, delay;
+
+	reg = (ddr3_read_pup_reg(PUP_WL_MODE, cs, pup) & 0x3FF);
+	delay = reg & PUP_DELAY_MASK;
+	reg |= ((dqs_delay + delay) << REG_PHY_DQS_REF_DLY_OFFS);
+	reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
+	reg |= (pup << REG_PHY_PUP_OFFS);
+	reg |= ((0x4 * cs + PUP_WL_MODE) << REG_PHY_CS_OFFS);
+
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+	do {
+		reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
+			REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+	} while (reg);	/* Wait for '0' to mark the end of the transaction */
+
+	udelay(10);
+}
+
+/*
+ * Set training patterns
+ */
+int ddr3_load_pbs_patterns(MV_DRAM_INFO *dram_info)
+{
+	u32 cs, cs_count, cs_tmp;
+	u32 sdram_addr;
+	u32 *pattern_ptr0, *pattern_ptr1;
+
+	/* Choose pattern */
+	switch (dram_info->ddr_width) {
+#if defined(MV88F672X)
+	case 16:
+		pattern_ptr0 = (u32 *)&pbs_pattern[0];
+		pattern_ptr1 = (u32 *)&pbs_pattern[1];
+		break;
+#endif
+	case 32:
+		pattern_ptr0 = (u32 *)&pbs_pattern_32b[0];
+		pattern_ptr1 = (u32 *)&pbs_pattern_32b[1];
+		break;
+#if defined(MV88F78X60)
+	case 64:
+		pattern_ptr0 = (u32 *)&pbs_pattern_64b[0];
+		pattern_ptr1 = (u32 *)&pbs_pattern_64b[1];
+		break;
+#endif
+	default:
+		return MV_FAIL;
+	}
+
+	/* Loop for each CS */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			cs_count = 0;
+			for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) {
+				if (dram_info->cs_ena & (1 << cs_tmp))
+					cs_count++;
+			}
+
+			/* Init PBS I pattern */
+			sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) +
+				      SDRAM_PBS_I_OFFS);
+			if (MV_OK !=
+			    ddr3_sdram_compare(dram_info, (u32) NULL, NULL,
+					       pattern_ptr0, LEN_STD_PATTERN,
+					       sdram_addr, 1, 0, NULL,
+					       0))
+				return MV_FAIL;
+
+			/* Init PBS II pattern */
+			sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) +
+				      SDRAM_PBS_II_OFFS);
+			if (MV_OK !=
+			    ddr3_sdram_compare(dram_info, (u32) NULL, NULL,
+					       pattern_ptr1, LEN_STD_PATTERN,
+					       sdram_addr, 1, 0, NULL,
+					       0))
+				return MV_FAIL;
+		}
+	}
+
+	return MV_OK;
+}
+#endif
diff --git a/drivers/ddr/marvell/axp/ddr3_read_leveling.c b/drivers/ddr/marvell/axp/ddr3_read_leveling.c
new file mode 100644
index 0000000..bf8d676
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_read_leveling.c
@@ -0,0 +1,1213 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_hw_training.h"
+
+/*
+ * Debug
+ */
+#define DEBUG_RL_C(s, d, l) \
+	DEBUG_RL_S(s); DEBUG_RL_D(d, l); DEBUG_RL_S("\n")
+#define DEBUG_RL_FULL_C(s, d, l) \
+	DEBUG_RL_FULL_S(s); DEBUG_RL_FULL_D(d, l); DEBUG_RL_FULL_S("\n")
+
+#ifdef MV_DEBUG_RL
+#define DEBUG_RL_S(s) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%s", s)
+#define DEBUG_RL_D(d, l) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%x", d)
+#else
+#define DEBUG_RL_S(s)
+#define DEBUG_RL_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_RL_FULL
+#define DEBUG_RL_FULL_S(s)		puts(s)
+#define DEBUG_RL_FULL_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_RL_FULL_S(s)
+#define DEBUG_RL_FULL_D(d, l)
+#endif
+
+extern u32 rl_pattern[LEN_STD_PATTERN];
+
+#ifdef RL_MODE
+static int ddr3_read_leveling_single_cs_rl_mode(u32 cs, u32 freq,
+						int ratio_2to1, u32 ecc,
+						MV_DRAM_INFO *dram_info);
+#else
+static int ddr3_read_leveling_single_cs_window_mode(u32 cs, u32 freq,
+						    int ratio_2to1, u32 ecc,
+						    MV_DRAM_INFO *dram_info);
+#endif
+
+/*
+ * Name:     ddr3_read_leveling_hw
+ * Desc:     Execute the Read leveling phase by HW
+ * Args:     dram_info - main struct
+ *           freq      - current sequence frequency
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_read_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info)
+{
+	u32 reg;
+
+	/* Debug message - Start Read leveling procedure */
+	DEBUG_RL_S("DDR3 - Read Leveling - Starting HW RL procedure\n");
+
+	/* Start Auto Read Leveling procedure */
+	reg = 1 << REG_DRAM_TRAINING_RL_OFFS;
+	/* Config the retest number */
+	reg |= (COUNT_HW_RL << REG_DRAM_TRAINING_RETEST_OFFS);
+
+	/* Enable CS in the automatic process */
+	reg |= (dram_info->cs_ena << REG_DRAM_TRAINING_CS_OFFS);
+
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) |
+		(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_SHADOW_ADDR, reg);
+
+	/* Wait */
+	do {
+		reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) &
+			(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	/* Check if Successful */
+	if (reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) &
+	    (1 << REG_DRAM_TRAINING_ERROR_OFFS)) {
+		u32 delay, phase, pup, cs;
+
+		dram_info->rl_max_phase = 0;
+		dram_info->rl_min_phase = 10;
+
+		/* Read results to arrays */
+		for (cs = 0; cs < MAX_CS; cs++) {
+			if (dram_info->cs_ena & (1 << cs)) {
+				for (pup = 0;
+				     pup < dram_info->num_of_total_pups;
+				     pup++) {
+					if (pup == dram_info->num_of_std_pups
+					    && dram_info->ecc_ena)
+						pup = ECC_PUP;
+					reg =
+					    ddr3_read_pup_reg(PUP_RL_MODE, cs,
+							      pup);
+					phase = (reg >> REG_PHY_PHASE_OFFS) &
+						PUP_PHASE_MASK;
+					delay = reg & PUP_DELAY_MASK;
+					dram_info->rl_val[cs][pup][P] = phase;
+					if (phase > dram_info->rl_max_phase)
+						dram_info->rl_max_phase = phase;
+					if (phase < dram_info->rl_min_phase)
+						dram_info->rl_min_phase = phase;
+					dram_info->rl_val[cs][pup][D] = delay;
+					dram_info->rl_val[cs][pup][S] =
+					    RL_FINAL_STATE;
+					reg =
+					    ddr3_read_pup_reg(PUP_RL_MODE + 0x1,
+							      cs, pup);
+					dram_info->rl_val[cs][pup][DQS] =
+					    (reg & 0x3F);
+				}
+#ifdef MV_DEBUG_RL
+				/* Print results */
+				DEBUG_RL_C("DDR3 - Read Leveling - Results for CS - ",
+					   (u32) cs, 1);
+
+				for (pup = 0;
+				     pup < (dram_info->num_of_total_pups);
+				     pup++) {
+					if (pup == dram_info->num_of_std_pups
+					    && dram_info->ecc_ena)
+						pup = ECC_PUP;
+					DEBUG_RL_S("DDR3 - Read Leveling - PUP: ");
+					DEBUG_RL_D((u32) pup, 1);
+					DEBUG_RL_S(", Phase: ");
+					DEBUG_RL_D((u32) dram_info->
+						   rl_val[cs][pup][P], 1);
+					DEBUG_RL_S(", Delay: ");
+					DEBUG_RL_D((u32) dram_info->
+						   rl_val[cs][pup][D], 2);
+					DEBUG_RL_S("\n");
+				}
+#endif
+			}
+		}
+
+		dram_info->rd_rdy_dly =
+			reg_read(REG_READ_DATA_READY_DELAYS_ADDR) &
+			REG_READ_DATA_SAMPLE_DELAYS_MASK;
+		dram_info->rd_smpl_dly =
+			reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR) &
+			REG_READ_DATA_READY_DELAYS_MASK;
+#ifdef MV_DEBUG_RL
+		DEBUG_RL_C("DDR3 - Read Leveling - Read Sample Delay: ",
+			   dram_info->rd_smpl_dly, 2);
+		DEBUG_RL_C("DDR3 - Read Leveling - Read Ready Delay: ",
+			   dram_info->rd_rdy_dly, 2);
+		DEBUG_RL_S("DDR3 - Read Leveling - HW RL Ended Successfully\n");
+#endif
+		return MV_OK;
+
+	} else {
+		DEBUG_RL_S("DDR3 - Read Leveling - HW RL Error\n");
+		return MV_FAIL;
+	}
+}
+
+/*
+ * Name:     ddr3_read_leveling_sw
+ * Desc:     Execute the Read leveling phase by SW
+ * Args:     dram_info - main struct
+ *           freq      - current sequence frequency
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_read_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info)
+{
+	u32 reg, cs, ecc, pup_num, phase, delay, pup;
+	int status;
+
+	/* Debug message - Start Read leveling procedure */
+	DEBUG_RL_S("DDR3 - Read Leveling - Starting SW RL procedure\n");
+
+	/* Enable SW Read Leveling */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_RL_MODE_OFFS);
+	/* [0]=1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+#ifdef RL_MODE
+	reg = (dram_info->cs_ena << REG_DRAM_TRAINING_CS_OFFS) |
+		(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+#endif
+
+	/* Loop for each CS */
+	for (cs = 0; cs < dram_info->num_cs; cs++) {
+		DEBUG_RL_C("DDR3 - Read Leveling - CS - ", (u32) cs, 1);
+
+		for (ecc = 0; ecc <= (dram_info->ecc_ena); ecc++) {
+			/* ECC Support - Switch ECC Mux on ecc=1 */
+			reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+				~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+			reg |= (dram_info->ecc_ena *
+				ecc << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+			reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+			if (ecc)
+				DEBUG_RL_S("DDR3 - Read Leveling - ECC Mux Enabled\n");
+			else
+				DEBUG_RL_S("DDR3 - Read Leveling - ECC Mux Disabled\n");
+
+			/* Set current sample delays */
+			reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+			reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK <<
+				 (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			reg |= (dram_info->cl <<
+				(REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+			reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg);
+
+			/* Set current Ready delay */
+			reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+			reg &= ~(REG_READ_DATA_READY_DELAYS_MASK <<
+				 (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+			if (!ratio_2to1) {
+				/* 1:1 mode */
+				reg |= ((dram_info->cl + 1) <<
+					(REG_READ_DATA_READY_DELAYS_OFFS * cs));
+			} else {
+				/* 2:1 mode */
+				reg |= ((dram_info->cl + 2) <<
+					(REG_READ_DATA_READY_DELAYS_OFFS * cs));
+			}
+			reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+
+			/* Read leveling Single CS[cs] */
+#ifdef RL_MODE
+			status =
+			    ddr3_read_leveling_single_cs_rl_mode(cs, freq,
+								 ratio_2to1,
+								 ecc,
+								 dram_info);
+			if (MV_OK != status)
+				return status;
+#else
+			status =
+			    ddr3_read_leveling_single_cs_window_mode(cs, freq,
+								     ratio_2to1,
+								     ecc,
+								     dram_info)
+			    if (MV_OK != status)
+				return status;
+#endif
+		}
+
+		/* Print results */
+		DEBUG_RL_C("DDR3 - Read Leveling - Results for CS - ", (u32) cs,
+			   1);
+
+		for (pup = 0;
+		     pup < (dram_info->num_of_std_pups + dram_info->ecc_ena);
+		     pup++) {
+			DEBUG_RL_S("DDR3 - Read Leveling - PUP: ");
+			DEBUG_RL_D((u32) pup, 1);
+			DEBUG_RL_S(", Phase: ");
+			DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][P], 1);
+			DEBUG_RL_S(", Delay: ");
+			DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][D], 2);
+			DEBUG_RL_S("\n");
+		}
+
+		DEBUG_RL_C("DDR3 - Read Leveling - Read Sample Delay: ",
+			   dram_info->rd_smpl_dly, 2);
+		DEBUG_RL_C("DDR3 - Read Leveling - Read Ready Delay: ",
+			   dram_info->rd_rdy_dly, 2);
+
+		/* Configure PHY with average of 3 locked leveling settings */
+		for (pup = 0;
+		     pup < (dram_info->num_of_std_pups + dram_info->ecc_ena);
+		     pup++) {
+			/* ECC support - bit 8 */
+			pup_num = (pup == dram_info->num_of_std_pups) ? ECC_BIT : pup;
+
+			/* For now, set last cnt result */
+			phase = dram_info->rl_val[cs][pup][P];
+			delay = dram_info->rl_val[cs][pup][D];
+			ddr3_write_pup_reg(PUP_RL_MODE, cs, pup_num, phase,
+					   delay);
+		}
+	}
+
+	/* Reset PHY read FIFO */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	do {
+		reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) &
+			(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	/* ECC Support - Switch ECC Mux off ecc=0 */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+		~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+#ifdef RL_MODE
+	reg_write(REG_DRAM_TRAINING_ADDR, 0);	/* 0x15B0 - Training Register */
+#endif
+
+	/* Disable SW Read Leveling */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+		~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* [0] = 0 - Disable SW override  */
+	reg = (reg | (0x1 << REG_DRAM_TRAINING_2_RL_MODE_OFFS));
+	/* [3] = 1 - Disable RL MODE */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	DEBUG_RL_S("DDR3 - Read Leveling - Finished RL procedure for all CS\n");
+	return MV_OK;
+}
+
+#ifdef RL_MODE
+/*
+ * overrun() extracted from ddr3_read_leveling_single_cs_rl_mode().
+ * This just got too much indented making it hard to read / edit.
+ */
+static void overrun(u32 cs, MV_DRAM_INFO *info, u32 pup, u32 locked_pups,
+		    u32 *locked_sum, u32 ecc, int *first_octet_locked,
+		    int *counter_in_progress, int final_delay, u32 delay,
+		    u32 phase)
+{
+	/* If no OverRun */
+	if (((~locked_pups >> pup) & 0x1) && (final_delay == 0)) {
+		int idx;
+
+		idx = pup + ecc * ECC_BIT;
+
+		/* PUP passed, start examining */
+		if (info->rl_val[cs][idx][S] == RL_UNLOCK_STATE) {
+			/* Must be RL_UNLOCK_STATE */
+			/* Match expected value ? - Update State Machine */
+			if (info->rl_val[cs][idx][C] < RL_RETRY_COUNT) {
+				DEBUG_RL_FULL_C("DDR3 - Read Leveling - We have no overrun and a match on pup: ",
+						(u32)pup, 1);
+				info->rl_val[cs][idx][C]++;
+
+				/* If pup got to last state - lock the delays */
+				if (info->rl_val[cs][idx][C] == RL_RETRY_COUNT) {
+					info->rl_val[cs][idx][C] = 0;
+					info->rl_val[cs][idx][DS] = delay;
+					info->rl_val[cs][idx][PS] = phase;
+
+					/* Go to Final State */
+					info->rl_val[cs][idx][S] = RL_FINAL_STATE;
+					*locked_sum = *locked_sum + 1;
+					DEBUG_RL_FULL_C("DDR3 - Read Leveling - We have locked pup: ",
+							(u32)pup, 1);
+
+					/*
+					 * If first lock - need to lock delays
+					 */
+					if (*first_octet_locked == 0) {
+						DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got first lock on pup: ",
+								(u32)pup, 1);
+						*first_octet_locked = 1;
+					}
+
+					/*
+					 * If pup is in not in final state but
+					 * there was match - dont increment
+					 * counter
+					 */
+				} else {
+					*counter_in_progress = 1;
+				}
+			}
+		}
+	}
+}
+
+/*
+ * Name:     ddr3_read_leveling_single_cs_rl_mode
+ * Desc:     Execute Read leveling for single Chip select
+ * Args:     cs        - current chip select
+ *           freq      - current sequence frequency
+ *           ecc       - ecc iteration indication
+ *           dram_info - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+static int ddr3_read_leveling_single_cs_rl_mode(u32 cs, u32 freq,
+						int ratio_2to1, u32 ecc,
+						MV_DRAM_INFO *dram_info)
+{
+	u32 reg, delay, phase, pup, rd_sample_delay, add, locked_pups,
+		repeat_max_cnt, sdram_offset, locked_sum;
+	u32 phase_min, ui_max_delay;
+	int all_locked, first_octet_locked, counter_in_progress;
+	int final_delay = 0;
+
+	DEBUG_RL_FULL_C("DDR3 - Read Leveling - Single CS - ", (u32) cs, 1);
+
+	/* Init values */
+	phase = 0;
+	delay = 0;
+	rd_sample_delay = dram_info->cl;
+	all_locked = 0;
+	first_octet_locked = 0;
+	repeat_max_cnt = 0;
+	locked_sum = 0;
+
+	for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc);
+	     pup++)
+		dram_info->rl_val[cs][pup + ecc * ECC_BIT][S] = 0;
+
+	/* Main loop */
+	while (!all_locked) {
+		counter_in_progress = 0;
+
+		DEBUG_RL_FULL_S("DDR3 - Read Leveling - RdSmplDly = ");
+		DEBUG_RL_FULL_D(rd_sample_delay, 2);
+		DEBUG_RL_FULL_S(", RdRdyDly = ");
+		DEBUG_RL_FULL_D(dram_info->rd_rdy_dly, 2);
+		DEBUG_RL_FULL_S(", Phase = ");
+		DEBUG_RL_FULL_D(phase, 1);
+		DEBUG_RL_FULL_S(", Delay = ");
+		DEBUG_RL_FULL_D(delay, 2);
+		DEBUG_RL_FULL_S("\n");
+
+		/*
+		 * Broadcast to all PUPs current RL delays: DQS phase,
+		 * leveling delay
+		 */
+		ddr3_write_pup_reg(PUP_RL_MODE, cs, PUP_BC, phase, delay);
+
+		/* Reset PHY read FIFO */
+		reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+			(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+		/* 0x15B8 - Training SW 2 Register */
+		reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+		do {
+			reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) &
+				(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+		} while (reg);	/* Wait for '0' */
+
+		/* Read pattern from SDRAM */
+		sdram_offset = cs * (SDRAM_CS_SIZE + 1) + SDRAM_RL_OFFS;
+		locked_pups = 0;
+		if (MV_OK !=
+		    ddr3_sdram_compare(dram_info, 0xFF, &locked_pups,
+				       rl_pattern, LEN_STD_PATTERN,
+				       sdram_offset, 0, 0, NULL, 0))
+			return MV_DDR3_TRAINING_ERR_RD_LVL_RL_PATTERN;
+
+		/* Octet evaluation */
+		/* pup_num = Q or 1 for ECC */
+		for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); pup++) {
+			/* Check Overrun */
+			if (!((reg_read(REG_DRAM_TRAINING_2_ADDR) >>
+			       (REG_DRAM_TRAINING_2_OVERRUN_OFFS + pup)) & 0x1)) {
+				overrun(cs, dram_info, pup, locked_pups,
+					&locked_sum, ecc, &first_octet_locked,
+					&counter_in_progress, final_delay,
+					delay, phase);
+			} else {
+				DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got overrun on pup: ",
+						(u32)pup, 1);
+			}
+		}
+
+		if (locked_sum == (dram_info->num_of_std_pups *
+				   (1 - ecc) + ecc)) {
+			all_locked = 1;
+			DEBUG_RL_FULL_S("DDR3 - Read Leveling - Single Cs - All pups locked\n");
+		}
+
+		/*
+		 * This is a fix for unstable condition where pups are
+		 * toggling between match and no match
+		 */
+		/*
+		 * If some of the pups is >1 <3, check if we did it too
+		 * many times
+		 */
+		if (counter_in_progress == 1) {
+			/* Notify at least one Counter is >=1 and < 3 */
+			if (repeat_max_cnt < RL_RETRY_COUNT) {
+				repeat_max_cnt++;
+				counter_in_progress = 1;
+				DEBUG_RL_FULL_S("DDR3 - Read Leveling - Counter is >=1 and <3\n");
+				DEBUG_RL_FULL_S("DDR3 - Read Leveling - So we will not increment the delay to see if locked again\n");
+			} else {
+				DEBUG_RL_FULL_S("DDR3 - Read Leveling - repeat_max_cnt reached max so now we will increment the delay\n");
+				counter_in_progress = 0;
+			}
+		}
+
+		/*
+		 * Check some of the pups are in the middle of state machine
+		 * and don't increment the delays
+		 */
+		if (!counter_in_progress && !all_locked) {
+			int idx;
+
+			idx = pup + ecc * ECC_BIT;
+
+			repeat_max_cnt = 0;
+			/* if 1:1 mode */
+			if ((!ratio_2to1) && ((phase == 0) || (phase == 4)))
+				ui_max_delay = MAX_DELAY_INV;
+			else
+				ui_max_delay = MAX_DELAY;
+
+			/* Increment Delay */
+			if (delay < ui_max_delay) {
+				delay++;
+				/*
+				 * Mark the last delay/pahse place for
+				 * window final place
+				 */
+				if (delay == ui_max_delay) {
+					if ((!ratio_2to1 && phase ==
+					     MAX_PHASE_RL_L_1TO1)
+					    || (ratio_2to1 && phase ==
+						MAX_PHASE_RL_L_2TO1))
+						final_delay = 1;
+				}
+			} else {
+				/* Phase+CL Incrementation */
+				delay = 0;
+
+				if (!ratio_2to1) {
+					/* 1:1 mode */
+					if (first_octet_locked) {
+						/* some Pup was Locked */
+						if (phase < MAX_PHASE_RL_L_1TO1) {
+							if (phase == 1) {
+								phase = 4;
+							} else {
+								phase++;
+								delay = MIN_DELAY_PHASE_1_LIMIT;
+							}
+						} else {
+							DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+							DEBUG_RL_S("1)DDR3 - Read Leveling - ERROR - NOT all PUPs Locked n");
+							return MV_DDR3_TRAINING_ERR_RD_LVL_RL_PUP_UNLOCK;
+						}
+					} else {
+						/* NO Pup was Locked */
+						if (phase < MAX_PHASE_RL_UL_1TO1) {
+							phase++;
+							delay =
+							    MIN_DELAY_PHASE_1_LIMIT;
+						} else {
+							phase = 0;
+						}
+					}
+				} else {
+					/* 2:1 mode */
+					if (first_octet_locked) {
+						/* some Pup was Locked */
+						if (phase < MAX_PHASE_RL_L_2TO1) {
+							phase++;
+						} else {
+							DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+							DEBUG_RL_S("2)DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+							for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); pup++) {
+								/* pup_num = Q or 1 for ECC */
+								if (dram_info->rl_val[cs][idx][S]
+								    == 0) {
+									DEBUG_RL_C("Failed byte is = ",
+										   pup, 1);
+								}
+							}
+							return MV_DDR3_TRAINING_ERR_RD_LVL_RL_PUP_UNLOCK;
+						}
+					} else {
+						/* No Pup was Locked */
+						if (phase < MAX_PHASE_RL_UL_2TO1)
+							phase++;
+						else
+							phase = 0;
+					}
+				}
+
+				/*
+				 * If we finished a full Phases cycle (so now
+				 * phase = 0, need to increment rd_sample_dly
+				 */
+				if (phase == 0 && first_octet_locked == 0) {
+					rd_sample_delay++;
+					if (rd_sample_delay == 0x10) {
+						DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+						DEBUG_RL_S("3)DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+						for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); pup++) {
+							/* pup_num = Q or 1 for ECC */
+							if (dram_info->
+							    rl_val[cs][idx][S] == 0) {
+								DEBUG_RL_C("Failed byte is = ",
+									   pup, 1);
+							}
+						}
+						return MV_DDR3_TRAINING_ERR_RD_LVL_PUP_UNLOCK;
+					}
+
+					/* Set current rd_sample_delay  */
+					reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+					reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK
+					      << (REG_READ_DATA_SAMPLE_DELAYS_OFFS
+						  * cs));
+					reg |= (rd_sample_delay <<
+						(REG_READ_DATA_SAMPLE_DELAYS_OFFS *
+						 cs));
+					reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR,
+						  reg);
+				}
+
+				/*
+				 * Set current rdReadyDelay according to the
+				 * hash table (Need to do this in every phase
+				 * change)
+				 */
+				if (!ratio_2to1) {
+					/* 1:1 mode */
+					add = reg_read(REG_TRAINING_DEBUG_2_ADDR);
+					switch (phase) {
+					case 0:
+						add = (add >>
+						       REG_TRAINING_DEBUG_2_OFFS);
+						break;
+					case 1:
+						add = (add >>
+						       (REG_TRAINING_DEBUG_2_OFFS
+							+ 3));
+						break;
+					case 4:
+						add = (add >>
+						       (REG_TRAINING_DEBUG_2_OFFS
+							+ 6));
+						break;
+					case 5:
+						add = (add >>
+						       (REG_TRAINING_DEBUG_2_OFFS
+							+ 9));
+						break;
+					}
+					add &= REG_TRAINING_DEBUG_2_MASK;
+				} else {
+					/* 2:1 mode */
+					add = reg_read(REG_TRAINING_DEBUG_3_ADDR);
+					add = (add >>
+					       (phase *
+						REG_TRAINING_DEBUG_3_OFFS));
+					add &= REG_TRAINING_DEBUG_3_MASK;
+				}
+
+				reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+				reg &= ~(REG_READ_DATA_READY_DELAYS_MASK <<
+					 (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+				reg |= ((rd_sample_delay + add) <<
+					(REG_READ_DATA_READY_DELAYS_OFFS * cs));
+				reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+				dram_info->rd_smpl_dly = rd_sample_delay;
+				dram_info->rd_rdy_dly = rd_sample_delay + add;
+			}
+
+			/* Reset counters for pups with states<RD_STATE_COUNT */
+			for (pup = 0; pup <
+				     (dram_info->num_of_std_pups * (1 - ecc) + ecc);
+			     pup++) {
+				if (dram_info->rl_val[cs][idx][C] < RL_RETRY_COUNT)
+					dram_info->rl_val[cs][idx][C] = 0;
+			}
+		}
+	}
+
+	phase_min = 10;
+
+	for (pup = 0; pup < (dram_info->num_of_std_pups); pup++) {
+		if (dram_info->rl_val[cs][pup][PS] < phase_min)
+			phase_min = dram_info->rl_val[cs][pup][PS];
+	}
+
+	/*
+	 * Set current rdReadyDelay according to the hash table (Need to
+	 * do this in every phase change)
+	 */
+	if (!ratio_2to1) {
+		/* 1:1 mode */
+		add = reg_read(REG_TRAINING_DEBUG_2_ADDR);
+		switch (phase_min) {
+		case 0:
+			add = (add >> REG_TRAINING_DEBUG_2_OFFS);
+			break;
+		case 1:
+			add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 3));
+			break;
+		case 4:
+			add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 6));
+			break;
+		case 5:
+			add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 9));
+			break;
+		}
+		add &= REG_TRAINING_DEBUG_2_MASK;
+	} else {
+		/* 2:1 mode */
+		add = reg_read(REG_TRAINING_DEBUG_3_ADDR);
+		add = (add >> (phase_min * REG_TRAINING_DEBUG_3_OFFS));
+		add &= REG_TRAINING_DEBUG_3_MASK;
+	}
+
+	reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+	reg &= ~(REG_READ_DATA_READY_DELAYS_MASK <<
+		 (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+	reg |= ((rd_sample_delay + add) << (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+	reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+	dram_info->rd_rdy_dly = rd_sample_delay + add;
+
+	for (cs = 0; cs < dram_info->num_cs; cs++) {
+		for (pup = 0; pup < dram_info->num_of_total_pups; pup++) {
+			reg = ddr3_read_pup_reg(PUP_RL_MODE + 0x1, cs, pup);
+			dram_info->rl_val[cs][pup][DQS] = (reg & 0x3F);
+		}
+	}
+
+	return MV_OK;
+}
+
+#else
+
+/*
+ * Name:     ddr3_read_leveling_single_cs_window_mode
+ * Desc:     Execute Read leveling for single Chip select
+ * Args:     cs        - current chip select
+ *           freq      - current sequence frequency
+ *           ecc       - ecc iteration indication
+ *           dram_info - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+static int ddr3_read_leveling_single_cs_window_mode(u32 cs, u32 freq,
+						    int ratio_2to1, u32 ecc,
+						    MV_DRAM_INFO *dram_info)
+{
+	u32 reg, delay, phase, sum, pup, rd_sample_delay, add, locked_pups,
+	    repeat_max_cnt, sdram_offset, final_sum, locked_sum;
+	u32 delay_s, delay_e, tmp, phase_min, ui_max_delay;
+	int all_locked, first_octet_locked, counter_in_progress;
+	int final_delay = 0;
+
+	DEBUG_RL_FULL_C("DDR3 - Read Leveling - Single CS - ", (u32) cs, 1);
+
+	/* Init values */
+	phase = 0;
+	delay = 0;
+	rd_sample_delay = dram_info->cl;
+	all_locked = 0;
+	first_octet_locked = 0;
+	repeat_max_cnt = 0;
+	sum = 0;
+	final_sum = 0;
+	locked_sum = 0;
+
+	for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc);
+	     pup++)
+		dram_info->rl_val[cs][pup + ecc * ECC_BIT][S] = 0;
+
+	/* Main loop */
+	while (!all_locked) {
+		counter_in_progress = 0;
+
+		DEBUG_RL_FULL_S("DDR3 - Read Leveling - RdSmplDly = ");
+		DEBUG_RL_FULL_D(rd_sample_delay, 2);
+		DEBUG_RL_FULL_S(", RdRdyDly = ");
+		DEBUG_RL_FULL_D(dram_info->rd_rdy_dly, 2);
+		DEBUG_RL_FULL_S(", Phase = ");
+		DEBUG_RL_FULL_D(phase, 1);
+		DEBUG_RL_FULL_S(", Delay = ");
+		DEBUG_RL_FULL_D(delay, 2);
+		DEBUG_RL_FULL_S("\n");
+
+		/*
+		 * Broadcast to all PUPs current RL delays: DQS phase,leveling
+		 * delay
+		 */
+		ddr3_write_pup_reg(PUP_RL_MODE, cs, PUP_BC, phase, delay);
+
+		/* Reset PHY read FIFO */
+		reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+			(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+		/* 0x15B8 - Training SW 2 Register */
+		reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+		do {
+			reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) &
+				(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+		} while (reg);	/* Wait for '0' */
+
+		/* Read pattern from SDRAM */
+		sdram_offset = cs * (SDRAM_CS_SIZE + 1) + SDRAM_RL_OFFS;
+		locked_pups = 0;
+		if (MV_OK !=
+		    ddr3_sdram_compare(dram_info, 0xFF, &locked_pups,
+				       rl_pattern, LEN_STD_PATTERN,
+				       sdram_offset, 0, 0, NULL, 0))
+			return MV_DDR3_TRAINING_ERR_RD_LVL_WIN_PATTERN;
+
+		/* Octet evaluation */
+		for (pup = 0; pup < (dram_info->num_of_std_pups *
+				     (1 - ecc) + ecc); pup++) {
+			/* pup_num = Q or 1 for ECC */
+			int idx;
+
+			idx = pup + ecc * ECC_BIT;
+
+			/* Check Overrun */
+			if (!((reg_read(REG_DRAM_TRAINING_2_ADDR) >>
+			      (REG_DRAM_TRAINING_2_OVERRUN_OFFS +
+			       pup)) & 0x1)) {
+				/* If no OverRun */
+
+				/* Inside the window */
+				if (dram_info->rl_val[cs][idx][S] == RL_WINDOW_STATE) {
+					/*
+					 * Match expected value ? - Update
+					 * State Machine
+					 */
+					if (((~locked_pups >> pup) & 0x1)
+					    && (final_delay == 0)) {
+						/* Match - Still inside the Window */
+						DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got another match inside the window  for pup: ",
+								(u32)pup, 1);
+
+					} else {
+						/* We got fail -> this is the end of the window */
+						dram_info->rl_val[cs][idx][DE] = delay;
+						dram_info->rl_val[cs][idx][PE] = phase;
+						/* Go to Final State */
+						dram_info->rl_val[cs][idx][S]++;
+						final_sum++;
+						DEBUG_RL_FULL_C("DDR3 - Read Leveling - We finished the window for pup: ",
+								(u32)pup, 1);
+					}
+
+					/* Before the start of the window */
+				} else if (dram_info->rl_val[cs][idx][S] ==
+					   RL_UNLOCK_STATE) {
+					/* Must be RL_UNLOCK_STATE */
+					/*
+					 * Match expected value ? - Update
+					 * State Machine
+					 */
+					if (dram_info->rl_val[cs][idx][C] <
+					    RL_RETRY_COUNT) {
+						if (((~locked_pups >> pup) & 0x1)) {
+							/* Match */
+							DEBUG_RL_FULL_C("DDR3 - Read Leveling - We have no overrun and a match on pup: ",
+									(u32)pup, 1);
+							dram_info->rl_val[cs][idx][C]++;
+
+							/* If pup got to last state - lock the delays */
+							if (dram_info->rl_val[cs][idx][C] ==
+							    RL_RETRY_COUNT) {
+								dram_info->rl_val[cs][idx][C] = 0;
+								dram_info->rl_val[cs][idx][DS] =
+									delay;
+								dram_info->rl_val[cs][idx][PS] =
+									phase;
+								dram_info->rl_val[cs][idx][S]++;	/* Go to Window State */
+								locked_sum++;
+								/* Will count the pups that got locked */
+
+								/* IF First lock - need to lock delays */
+								if (first_octet_locked == 0) {
+									DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got first lock on pup: ",
+											(u32)pup, 1);
+									first_octet_locked
+									    =
+									    1;
+								}
+							}
+
+							/* if pup is in not in final state but there was match - dont increment counter */
+							else {
+								counter_in_progress
+								    = 1;
+							}
+						}
+					}
+				}
+			} else {
+				DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got overrun on pup: ",
+						(u32)pup, 1);
+				counter_in_progress = 1;
+			}
+		}
+
+		if (final_sum == (dram_info->num_of_std_pups * (1 - ecc) + ecc)) {
+			all_locked = 1;
+			DEBUG_RL_FULL_S("DDR3 - Read Leveling - Single Cs - All pups locked\n");
+		}
+
+		/*
+		 * This is a fix for unstable condition where pups are
+		 * toggling between match and no match
+		 */
+		/*
+		 * If some of the pups is >1 <3, check if we did it too many
+		 * times
+		 */
+		if (counter_in_progress == 1) {
+			if (repeat_max_cnt < RL_RETRY_COUNT) {
+				/* Notify at least one Counter is >=1 and < 3 */
+				repeat_max_cnt++;
+				counter_in_progress = 1;
+				DEBUG_RL_FULL_S("DDR3 - Read Leveling - Counter is >=1 and <3\n");
+				DEBUG_RL_FULL_S("DDR3 - Read Leveling - So we will not increment the delay to see if locked again\n");
+			} else {
+				DEBUG_RL_FULL_S("DDR3 - Read Leveling - repeat_max_cnt reached max so now we will increment the delay\n");
+				counter_in_progress = 0;
+			}
+		}
+
+		/*
+		 * Check some of the pups are in the middle of state machine
+		 * and don't increment the delays
+		 */
+		if (!counter_in_progress && !all_locked) {
+			repeat_max_cnt = 0;
+			if (!ratio_2to1)
+				ui_max_delay = MAX_DELAY_INV;
+			else
+				ui_max_delay = MAX_DELAY;
+
+			/* Increment Delay */
+			if (delay < ui_max_delay) {
+				/* Delay Incrementation */
+				delay++;
+				if (delay == ui_max_delay) {
+					/*
+					 * Mark the last delay/pahse place
+					 * for window final place
+					 */
+					if ((!ratio_2to1
+					     && phase == MAX_PHASE_RL_L_1TO1)
+					    || (ratio_2to1
+						&& phase ==
+						MAX_PHASE_RL_L_2TO1))
+						final_delay = 1;
+				}
+			} else {
+				/* Phase+CL Incrementation */
+				delay = 0;
+				if (!ratio_2to1) {
+					/* 1:1 mode */
+					if (first_octet_locked) {
+						/* some pupet was Locked */
+						if (phase < MAX_PHASE_RL_L_1TO1) {
+#ifdef RL_WINDOW_WA
+							if (phase == 0)
+#else
+							if (phase == 1)
+#endif
+								phase = 4;
+							else
+								phase++;
+						} else {
+							DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+							return MV_DDR3_TRAINING_ERR_RD_LVL_WIN_PUP_UNLOCK;
+						}
+					} else {
+						/* No Pup was Locked */
+						if (phase < MAX_PHASE_RL_UL_1TO1) {
+#ifdef RL_WINDOW_WA
+							if (phase == 0)
+								phase = 4;
+#else
+							phase++;
+#endif
+						} else
+							phase = 0;
+					}
+				} else {
+					/* 2:1 mode */
+					if (first_octet_locked) {
+						/* Some Pup was Locked */
+						if (phase < MAX_PHASE_RL_L_2TO1) {
+							phase++;
+						} else {
+							DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n");
+							return MV_DDR3_TRAINING_ERR_RD_LVL_WIN_PUP_UNLOCK;
+						}
+					} else {
+						/* No Pup was Locked */
+						if (phase < MAX_PHASE_RL_UL_2TO1)
+							phase++;
+						else
+							phase = 0;
+					}
+				}
+
+				/*
+				 * If we finished a full Phases cycle (so
+				 * now phase = 0, need to increment
+				 * rd_sample_dly
+				 */
+				if (phase == 0 && first_octet_locked == 0) {
+					rd_sample_delay++;
+
+					/* Set current rd_sample_delay  */
+					reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
+					reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK <<
+						 (REG_READ_DATA_SAMPLE_DELAYS_OFFS
+						  * cs));
+					reg |= (rd_sample_delay <<
+						(REG_READ_DATA_SAMPLE_DELAYS_OFFS *
+						 cs));
+					reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR,
+						  reg);
+				}
+
+				/*
+				 * Set current rdReadyDelay according to the
+				 * hash table (Need to do this in every phase
+				 * change)
+				 */
+				if (!ratio_2to1) {
+					/* 1:1 mode */
+					add = reg_read(REG_TRAINING_DEBUG_2_ADDR);
+					switch (phase) {
+					case 0:
+						add = add >>
+							REG_TRAINING_DEBUG_2_OFFS;
+						break;
+					case 1:
+						add = add >>
+							(REG_TRAINING_DEBUG_2_OFFS
+							 + 3);
+						break;
+					case 4:
+						add = add >>
+							(REG_TRAINING_DEBUG_2_OFFS
+							 + 6);
+						break;
+					case 5:
+						add = add >>
+							(REG_TRAINING_DEBUG_2_OFFS
+							 + 9);
+						break;
+					}
+				} else {
+					/* 2:1 mode */
+					add = reg_read(REG_TRAINING_DEBUG_3_ADDR);
+					add = (add >> phase *
+					       REG_TRAINING_DEBUG_3_OFFS);
+				}
+				add &= REG_TRAINING_DEBUG_2_MASK;
+				reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+				reg &= ~(REG_READ_DATA_READY_DELAYS_MASK <<
+					 (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+				reg |= ((rd_sample_delay + add) <<
+					(REG_READ_DATA_READY_DELAYS_OFFS * cs));
+				reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+				dram_info->rd_smpl_dly = rd_sample_delay;
+				dram_info->rd_rdy_dly = rd_sample_delay + add;
+			}
+
+			/* Reset counters for pups with states<RD_STATE_COUNT */
+			for (pup = 0;
+			     pup <
+			     (dram_info->num_of_std_pups * (1 - ecc) + ecc);
+			     pup++) {
+				if (dram_info->rl_val[cs][idx][C] < RL_RETRY_COUNT)
+					dram_info->rl_val[cs][idx][C] = 0;
+			}
+		}
+	}
+
+	phase_min = 10;
+
+	for (pup = 0; pup < (dram_info->num_of_std_pups); pup++) {
+		DEBUG_RL_S("DDR3 - Read Leveling - Window info - PUP: ");
+		DEBUG_RL_D((u32) pup, 1);
+		DEBUG_RL_S(", PS: ");
+		DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][PS], 1);
+		DEBUG_RL_S(", DS: ");
+		DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][DS], 2);
+		DEBUG_RL_S(", PE: ");
+		DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][PE], 1);
+		DEBUG_RL_S(", DE: ");
+		DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][DE], 2);
+		DEBUG_RL_S("\n");
+	}
+
+	/* Find center of the window procedure */
+	for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc);
+	     pup++) {
+#ifdef RL_WINDOW_WA
+		if (!ratio_2to1) {	/* 1:1 mode */
+			if (dram_info->rl_val[cs][idx][PS] == 4)
+				dram_info->rl_val[cs][idx][PS] = 1;
+			if (dram_info->rl_val[cs][idx][PE] == 4)
+				dram_info->rl_val[cs][idx][PE] = 1;
+
+			delay_s = dram_info->rl_val[cs][idx][PS] *
+				MAX_DELAY_INV + dram_info->rl_val[cs][idx][DS];
+			delay_e = dram_info->rl_val[cs][idx][PE] *
+				MAX_DELAY_INV + dram_info->rl_val[cs][idx][DE];
+
+			tmp = (delay_e - delay_s) / 2 + delay_s;
+			phase = tmp / MAX_DELAY_INV;
+			if (phase == 1)	/* 1:1 mode */
+				phase = 4;
+
+			if (phase < phase_min)	/* for the read ready delay */
+				phase_min = phase;
+
+			dram_info->rl_val[cs][idx][P] = phase;
+			dram_info->rl_val[cs][idx][D] = tmp % MAX_DELAY_INV;
+
+		} else {
+			delay_s = dram_info->rl_val[cs][idx][PS] *
+				MAX_DELAY + dram_info->rl_val[cs][idx][DS];
+			delay_e = dram_info->rl_val[cs][idx][PE] *
+				MAX_DELAY + dram_info->rl_val[cs][idx][DE];
+
+			tmp = (delay_e - delay_s) / 2 + delay_s;
+			phase = tmp / MAX_DELAY;
+
+			if (phase < phase_min)	/* for the read ready delay */
+				phase_min = phase;
+
+			dram_info->rl_val[cs][idx][P] = phase;
+			dram_info->rl_val[cs][idx][D] = tmp % MAX_DELAY;
+		}
+#else
+		if (!ratio_2to1) {	/* 1:1 mode */
+			if (dram_info->rl_val[cs][idx][PS] > 1)
+				dram_info->rl_val[cs][idx][PS] -= 2;
+			if (dram_info->rl_val[cs][idx][PE] > 1)
+				dram_info->rl_val[cs][idx][PE] -= 2;
+		}
+
+		delay_s = dram_info->rl_val[cs][idx][PS] * MAX_DELAY +
+			dram_info->rl_val[cs][idx][DS];
+		delay_e = dram_info->rl_val[cs][idx][PE] * MAX_DELAY +
+			dram_info->rl_val[cs][idx][DE];
+
+		tmp = (delay_e - delay_s) / 2 + delay_s;
+		phase = tmp / MAX_DELAY;
+		if (!ratio_2to1 && phase > 1)	/* 1:1 mode */
+			phase += 2;
+
+		if (phase < phase_min)	/* for the read ready delay */
+			phase_min = phase;
+
+		dram_info->rl_val[cs][idx][P] = phase;
+		dram_info->rl_val[cs][idx][D] = tmp % MAX_DELAY;
+#endif
+	}
+
+	/* Set current rdReadyDelay according to the hash table (Need to do this in every phase change) */
+	if (!ratio_2to1) {	/* 1:1 mode */
+		add = reg_read(REG_TRAINING_DEBUG_2_ADDR);
+		switch (phase_min) {
+		case 0:
+			add = (add >> REG_TRAINING_DEBUG_2_OFFS);
+			break;
+		case 1:
+			add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 3));
+			break;
+		case 4:
+			add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 6));
+			break;
+		case 5:
+			add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 9));
+			break;
+		}
+	} else {		/* 2:1 mode */
+		add = reg_read(REG_TRAINING_DEBUG_3_ADDR);
+		add = (add >> phase_min * REG_TRAINING_DEBUG_3_OFFS);
+	}
+
+	add &= REG_TRAINING_DEBUG_2_MASK;
+	reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR);
+	reg &=
+	    ~(REG_READ_DATA_READY_DELAYS_MASK <<
+	      (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+	reg |=
+	    ((rd_sample_delay + add) << (REG_READ_DATA_READY_DELAYS_OFFS * cs));
+	reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+	dram_info->rd_rdy_dly = rd_sample_delay + add;
+
+	for (cs = 0; cs < dram_info->num_cs; cs++) {
+		for (pup = 0; pup < dram_info->num_of_total_pups; pup++) {
+			reg = ddr3_read_pup_reg(PUP_RL_MODE + 0x1, cs, pup);
+			dram_info->rl_val[cs][pup][DQS] = (reg & 0x3F);
+		}
+	}
+
+	return MV_OK;
+}
+#endif
diff --git a/drivers/ddr/marvell/axp/ddr3_sdram.c b/drivers/ddr/marvell/axp/ddr3_sdram.c
new file mode 100644
index 0000000..3a266c6
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_sdram.c
@@ -0,0 +1,668 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_hw_training.h"
+#include "xor.h"
+#include "xor_regs.h"
+
+static void ddr3_flush_l1_line(u32 line);
+
+extern u32 pbs_pattern[2][LEN_16BIT_PBS_PATTERN];
+extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN];
+#if defined(MV88F78X60)
+extern u32 pbs_pattern_64b[2][LEN_PBS_PATTERN];
+#endif
+extern u32 pbs_dq_mapping[PUP_NUM_64BIT][DQ_NUM];
+
+#if defined(MV88F78X60) || defined(MV88F672X)
+/* PBS locked dq (per pup) */
+u32 pbs_locked_dq[MAX_PUP_NUM][DQ_NUM] = { { 0 } };
+u32 pbs_locked_dm[MAX_PUP_NUM] = { 0 };
+u32 pbs_locked_value[MAX_PUP_NUM][DQ_NUM] = { { 0 } };
+
+int per_bit_data[MAX_PUP_NUM][DQ_NUM];
+#endif
+
+static u32 sdram_data[LEN_KILLER_PATTERN] __aligned(32) = { 0 };
+
+static struct crc_dma_desc dma_desc __aligned(32) = { 0 };
+
+#define XOR_TIMEOUT 0x8000000
+
+struct xor_channel_t {
+	struct crc_dma_desc *desc;
+	unsigned long desc_phys_addr;
+};
+
+#define XOR_CAUSE_DONE_MASK(chan)	((0x1 | 0x2) << (chan * 16))
+
+void xor_waiton_eng(int chan)
+{
+	int timeout;
+
+	timeout = 0;
+	while (!(reg_read(XOR_CAUSE_REG(XOR_UNIT(chan))) &
+		 XOR_CAUSE_DONE_MASK(XOR_CHAN(chan)))) {
+		if (timeout > XOR_TIMEOUT)
+			goto timeout;
+
+		timeout++;
+	}
+
+	timeout = 0;
+	while (mv_xor_state_get(chan) != MV_IDLE) {
+		if (timeout > XOR_TIMEOUT)
+			goto timeout;
+
+		timeout++;
+	}
+
+	/* Clear int */
+	reg_write(XOR_CAUSE_REG(XOR_UNIT(chan)),
+		  ~(XOR_CAUSE_DONE_MASK(XOR_CHAN(chan))));
+
+timeout:
+	return;
+}
+
+static int special_compare_pattern(u32 uj)
+{
+	if ((uj == 30) || (uj == 31) || (uj == 61) || (uj == 62) ||
+	    (uj == 93) || (uj == 94) || (uj == 126) || (uj == 127))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Compare code extracted as its used by multiple functions. This
+ * reduces code-size and makes it easier to maintain it. Additionally
+ * the code is not indented that much and therefore easier to read.
+ */
+static void compare_pattern_v1(u32 uj, u32 *pup, u32 *pattern,
+			       u32 pup_groups, int debug_dqs)
+{
+	u32 val;
+	u32 uk;
+	u32 var1;
+	u32 var2;
+	__maybe_unused u32 dq;
+
+	if (((sdram_data[uj]) != (pattern[uj])) && (*pup != 0xFF)) {
+		for (uk = 0; uk < PUP_NUM_32BIT; uk++) {
+			val = CMP_BYTE_SHIFT * uk;
+			var1 = ((sdram_data[uj] >> val) & CMP_BYTE_MASK);
+			var2 = ((pattern[uj] >> val) & CMP_BYTE_MASK);
+
+			if (var1 != var2) {
+				*pup |= (1 << (uk + (PUP_NUM_32BIT *
+						     (uj % pup_groups))));
+
+#ifdef MV_DEBUG_DQS
+				if (!debug_dqs)
+					continue;
+
+				for (dq = 0; dq < DQ_NUM; dq++) {
+					val = uk + (PUP_NUM_32BIT *
+						    (uj % pup_groups));
+					if (((var1 >> dq) & 0x1) !=
+					    ((var2 >> dq) & 0x1))
+						per_bit_data[val][dq] = 1;
+					else
+						per_bit_data[val][dq] = 0;
+				}
+#endif
+			}
+		}
+	}
+}
+
+static void compare_pattern_v2(u32 uj, u32 *pup, u32 *pattern)
+{
+	u32 val;
+	u32 uk;
+	u32 var1;
+	u32 var2;
+
+	if (((sdram_data[uj]) != (pattern[uj])) && (*pup != 0x3)) {
+		/* Found error */
+		for (uk = 0; uk < PUP_NUM_32BIT; uk++) {
+			val = CMP_BYTE_SHIFT * uk;
+			var1 = (sdram_data[uj] >> val) & CMP_BYTE_MASK;
+			var2 = (pattern[uj] >> val) & CMP_BYTE_MASK;
+			if (var1 != var2)
+				*pup |= (1 << (uk % PUP_NUM_16BIT));
+		}
+	}
+}
+
+/*
+ * Name:     ddr3_sdram_compare
+ * Desc:     Execute compare per PUP
+ * Args:     unlock_pup      Bit array of the unlock pups
+ *           new_locked_pup  Output  bit array of the pups with failed compare
+ *           pattern         Pattern to compare
+ *           pattern_len     Length of pattern (in bytes)
+ *           sdram_offset    offset address to the SDRAM
+ *           write           write to the SDRAM before read
+ *           mask            compare pattern with mask;
+ *           mask_pattern    Mask to compare pattern
+ *
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_sdram_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+		       u32 *new_locked_pup, u32 *pattern,
+		       u32 pattern_len, u32 sdram_offset, int write,
+		       int mask, u32 *mask_pattern,
+		       int special_compare)
+{
+	u32 uj;
+	__maybe_unused u32 pup_groups;
+	__maybe_unused u32 dq;
+
+#if !defined(MV88F67XX)
+	if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
+		pup_groups = 2;
+	else
+		pup_groups = 1;
+#endif
+
+	ddr3_reset_phy_read_fifo();
+
+	/* Check if need to write to sdram before read */
+	if (write == 1)
+		ddr3_dram_sram_burst((u32)pattern, sdram_offset, pattern_len);
+
+	ddr3_dram_sram_burst(sdram_offset, (u32)sdram_data, pattern_len);
+
+	/* Compare read result to write */
+	for (uj = 0; uj < pattern_len; uj++) {
+		if (special_compare && special_compare_pattern(uj))
+			continue;
+
+#if defined(MV88F78X60) || defined(MV88F672X)
+		compare_pattern_v1(uj, new_locked_pup, pattern, pup_groups, 1);
+#elif defined(MV88F67XX)
+		compare_pattern_v2(uj, new_locked_pup, pattern);
+#endif
+	}
+
+	return MV_OK;
+}
+
+#if defined(MV88F78X60) || defined(MV88F672X)
+/*
+ * Name:     ddr3_sdram_dm_compare
+ * Desc:     Execute compare per PUP
+ * Args:     unlock_pup      Bit array of the unlock pups
+ *           new_locked_pup  Output  bit array of the pups with failed compare
+ *           pattern         Pattern to compare
+ *           pattern_len     Length of pattern (in bytes)
+ *           sdram_offset    offset address to the SDRAM
+ *           write           write to the SDRAM before read
+ *           mask            compare pattern with mask;
+ *           mask_pattern    Mask to compare pattern
+ *
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_sdram_dm_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+			  u32 *new_locked_pup, u32 *pattern,
+			  u32 sdram_offset)
+{
+	u32 uj, uk, var1, var2, pup_groups;
+	u32 val;
+	u32 pup = 0;
+
+	if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
+		pup_groups = 2;
+	else
+		pup_groups = 1;
+
+	ddr3_dram_sram_burst((u32)pattern, SDRAM_PBS_TX_OFFS,
+			     LEN_PBS_PATTERN);
+	ddr3_dram_sram_burst(SDRAM_PBS_TX_OFFS, (u32)sdram_data,
+			     LEN_PBS_PATTERN);
+
+	/* Validate the correctness of the results */
+	for (uj = 0; uj < LEN_PBS_PATTERN; uj++)
+		compare_pattern_v1(uj, &pup, pattern, pup_groups, 0);
+
+	/* Test the DM Signals */
+	*(u32 *)(SDRAM_PBS_TX_OFFS + 0x10) = 0x12345678;
+	*(u32 *)(SDRAM_PBS_TX_OFFS + 0x14) = 0x12345678;
+
+	sdram_data[0] = *(u32 *)(SDRAM_PBS_TX_OFFS + 0x10);
+	sdram_data[1] = *(u32 *)(SDRAM_PBS_TX_OFFS + 0x14);
+
+	for (uj = 0; uj < 2; uj++) {
+		if (((sdram_data[uj]) != (pattern[uj])) &&
+		    (*new_locked_pup != 0xFF)) {
+			for (uk = 0; uk < PUP_NUM_32BIT; uk++) {
+				val = CMP_BYTE_SHIFT * uk;
+				var1 = ((sdram_data[uj] >> val) & CMP_BYTE_MASK);
+				var2 = ((pattern[uj] >> val) & CMP_BYTE_MASK);
+				if (var1 != var2) {
+					*new_locked_pup |= (1 << (uk +
+						(PUP_NUM_32BIT * (uj % pup_groups))));
+					*new_locked_pup |= pup;
+				}
+			}
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_sdram_pbs_compare
+ * Desc:     Execute SRAM compare per PUP and DQ.
+ * Args:     pup_locked             bit array of locked pups
+ *           is_tx                  Indicate whether Rx or Tx
+ *           pbs_pattern_idx        Index of PBS pattern
+ *           pbs_curr_val           The PBS value
+ *           pbs_lock_val           The value to set to locked PBS
+ *           skew_array             Global array to update with the compare results
+ *           ai_unlock_pup_dq_array bit array of the locked / unlocked pups per dq.
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_sdram_pbs_compare(MV_DRAM_INFO *dram_info, u32 pup_locked,
+			   int is_tx, u32 pbs_pattern_idx,
+			   u32 pbs_curr_val, u32 pbs_lock_val,
+			   u32 *skew_array, u8 *unlock_pup_dq_array,
+			   u32 ecc)
+{
+	/* bit array failed dq per pup for current compare */
+	u32 pbs_write_pup[DQ_NUM] = { 0 };
+	u32 update_pup;	/* pup as HW convention */
+	u32 max_pup;	/* maximal pup index */
+	u32 pup_addr;
+	u32 ui, dq, pup;
+	int var1, var2;
+	u32 sdram_offset, pup_groups, tmp_pup;
+	u32 *pattern_ptr;
+	u32 val;
+
+	/* Choose pattern */
+	switch (dram_info->ddr_width) {
+#if defined(MV88F672X)
+	case 16:
+		pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx];
+		break;
+#endif
+	case 32:
+		pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx];
+		break;
+#if defined(MV88F78X60)
+	case 64:
+		pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx];
+		break;
+#endif
+	default:
+		return MV_FAIL;
+	}
+
+	max_pup = dram_info->num_of_std_pups;
+
+	sdram_offset = SDRAM_PBS_I_OFFS + pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS;
+
+	if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
+		pup_groups = 2;
+	else
+		pup_groups = 1;
+
+	ddr3_reset_phy_read_fifo();
+
+	/* Check if need to write to sdram before read */
+	if (is_tx == 1) {
+		ddr3_dram_sram_burst((u32)pattern_ptr, sdram_offset,
+				     LEN_PBS_PATTERN);
+	}
+
+	ddr3_dram_sram_read(sdram_offset, (u32)sdram_data, LEN_PBS_PATTERN);
+
+	/* Compare read result to write */
+	for (ui = 0; ui < LEN_PBS_PATTERN; ui++) {
+		if ((sdram_data[ui]) != (pattern_ptr[ui])) {
+			/* found error */
+			/* error in low pup group */
+			for (pup = 0; pup < PUP_NUM_32BIT; pup++) {
+				val = CMP_BYTE_SHIFT * pup;
+				var1 = ((sdram_data[ui] >> val) &
+					CMP_BYTE_MASK);
+				var2 = ((pattern_ptr[ui] >> val) &
+					CMP_BYTE_MASK);
+
+				if (var1 != var2) {
+					if (dram_info->ddr_width > 16) {
+						tmp_pup = (pup + PUP_NUM_32BIT *
+							   (ui % pup_groups));
+					} else {
+						tmp_pup = (pup % PUP_NUM_16BIT);
+					}
+
+					update_pup = (1 << tmp_pup);
+					if (ecc && (update_pup != 0x1))
+						continue;
+
+					/*
+					 * Pup is failed - Go over all DQs and
+					 * look for failures
+					 */
+					for (dq = 0; dq < DQ_NUM; dq++) {
+						val = tmp_pup * (1 - ecc) +
+							ecc * ECC_PUP;
+						if (((var1 >> dq) & 0x1) !=
+						    ((var2 >> dq) & 0x1)) {
+							if (pbs_locked_dq[val][dq] == 1 &&
+							    pbs_locked_value[val][dq] != pbs_curr_val)
+								continue;
+
+							/*
+							 * Activate write to
+							 * update PBS to
+							 * pbs_lock_val
+							 */
+							pbs_write_pup[dq] |=
+								update_pup;
+
+							/*
+							 * Update the
+							 * unlock_pup_dq_array
+							 */
+							unlock_pup_dq_array[dq] &=
+								~update_pup;
+
+							/*
+							 * Lock PBS value for
+							 * failed bits in
+							 * compare operation
+							 */
+							skew_array[tmp_pup * DQ_NUM + dq] =
+								pbs_curr_val;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	pup_addr = (is_tx == 1) ? PUP_PBS_TX : PUP_PBS_RX;
+
+	/* Set last failed bits PBS to min / max pbs value */
+	for (dq = 0; dq < DQ_NUM; dq++) {
+		for (pup = 0; pup < max_pup; pup++) {
+			if (pbs_write_pup[dq] & (1 << pup)) {
+				val = pup * (1 - ecc) + ecc * ECC_PUP;
+				if (pbs_locked_dq[val][dq] == 1 &&
+				    pbs_locked_value[val][dq] != pbs_curr_val)
+					continue;
+
+				/* Mark the dq as locked */
+				pbs_locked_dq[val][dq] = 1;
+				pbs_locked_value[val][dq] = pbs_curr_val;
+				ddr3_write_pup_reg(pup_addr +
+						   pbs_dq_mapping[val][dq],
+						   CS0, val, 0, pbs_lock_val);
+			}
+		}
+	}
+
+	return MV_OK;
+}
+#endif
+
+/*
+ * Name:     ddr3_sdram_direct_compare
+ * Desc:     Execute compare  per PUP without DMA (no burst mode)
+ * Args:     unlock_pup       Bit array of the unlock pups
+ *           new_locked_pup   Output  bit array of the pups with failed compare
+ *           pattern          Pattern to compare
+ *           pattern_len      Length of pattern (in bytes)
+ *           sdram_offset     offset address to the SDRAM
+ *           write            write to the SDRAM before read
+ *           mask             compare pattern with mask;
+ *           auiMaskPatter    Mask to compare pattern
+ *
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_sdram_direct_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+			      u32 *new_locked_pup, u32 *pattern,
+			      u32 pattern_len, u32 sdram_offset,
+			      int write, int mask, u32 *mask_pattern)
+{
+	u32 uj, uk, pup_groups;
+	u32 *sdram_addr;	/* used to read from SDRAM */
+
+	sdram_addr = (u32 *)sdram_offset;
+
+	if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
+		pup_groups = 2;
+	else
+		pup_groups = 1;
+
+	/* Check if need to write before read */
+	if (write == 1) {
+		for (uk = 0; uk < pattern_len; uk++) {
+			*sdram_addr = pattern[uk];
+			sdram_addr++;
+		}
+	}
+
+	sdram_addr = (u32 *)sdram_offset;
+
+	for (uk = 0; uk < pattern_len; uk++) {
+		sdram_data[uk] = *sdram_addr;
+		sdram_addr++;
+	}
+
+	/* Compare read result to write */
+	for (uj = 0; uj < pattern_len; uj++) {
+		if (dram_info->ddr_width > 16) {
+			compare_pattern_v1(uj, new_locked_pup, pattern,
+					   pup_groups, 0);
+		} else {
+			compare_pattern_v2(uj, new_locked_pup, pattern);
+		}
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_dram_sram_burst
+ * Desc:     Read from the SDRAM in burst of 64 bytes
+ * Args:     src
+ *           dst
+ * Notes:    Using the XOR mechanism
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+int ddr3_dram_sram_burst(u32 src, u32 dst, u32 len)
+{
+	u32 chan, byte_count, cs_num, byte;
+	struct xor_channel_t channel;
+
+	chan = 0;
+	byte_count = len * 4;
+
+	/* Wait for previous transfer completion */
+	while (mv_xor_state_get(chan) != MV_IDLE)
+		;
+
+	/* Build the channel descriptor */
+	channel.desc = &dma_desc;
+
+	/* Enable Address Override and set correct src and dst */
+	if (src < SRAM_BASE) {
+		/* src is DRAM CS, dst is SRAM */
+		cs_num = (src / (1 + SDRAM_CS_SIZE));
+		reg_write(XOR_ADDR_OVRD_REG(0, 0),
+			  ((cs_num << 1) | (1 << 0)));
+		channel.desc->src_addr0 = (src % (1 + SDRAM_CS_SIZE));
+		channel.desc->dst_addr = dst;
+	} else {
+		/* src is SRAM, dst is DRAM CS */
+		cs_num = (dst / (1 + SDRAM_CS_SIZE));
+		reg_write(XOR_ADDR_OVRD_REG(0, 0),
+			  ((cs_num << 25) | (1 << 24)));
+		channel.desc->src_addr0 = (src);
+		channel.desc->dst_addr = (dst % (1 + SDRAM_CS_SIZE));
+		channel.desc->src_addr0 = src;
+		channel.desc->dst_addr = (dst % (1 + SDRAM_CS_SIZE));
+	}
+
+	channel.desc->src_addr1 = 0;
+	channel.desc->byte_cnt = byte_count;
+	channel.desc->next_desc_ptr = 0;
+	channel.desc->status = 1 << 31;
+	channel.desc->desc_cmd = 0x0;
+	channel.desc_phys_addr = (unsigned long)&dma_desc;
+
+	ddr3_flush_l1_line((u32)&dma_desc);
+
+	/* Issue the transfer */
+	if (mv_xor_transfer(chan, MV_DMA, channel.desc_phys_addr) != MV_OK)
+		return MV_FAIL;
+
+	/* Wait for completion */
+	xor_waiton_eng(chan);
+
+	if (dst > SRAM_BASE) {
+		for (byte = 0; byte < byte_count; byte += 0x20)
+			cache_inv(dst + byte);
+	}
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_flush_l1_line
+ * Desc:
+ * Args:
+ * Notes:
+ * Returns:  MV_OK if success, other error code if fail.
+ */
+static void ddr3_flush_l1_line(u32 line)
+{
+	u32 reg;
+
+#if defined(MV88F672X)
+	reg = 1;
+#else
+	reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR) &
+		(1 << REG_SAMPLE_RESET_CPU_ARCH_OFFS);
+#ifdef MV88F67XX
+	reg = ~reg & (1 << REG_SAMPLE_RESET_CPU_ARCH_OFFS);
+#endif
+#endif
+
+	if (reg) {
+		/* V7 Arch mode */
+		flush_l1_v7(line);
+		flush_l1_v7(line + CACHE_LINE_SIZE);
+	} else {
+		/* V6 Arch mode */
+		flush_l1_v6(line);
+		flush_l1_v6(line + CACHE_LINE_SIZE);
+	}
+}
+
+int ddr3_dram_sram_read(u32 src, u32 dst, u32 len)
+{
+	u32 ui;
+	u32 *dst_ptr, *src_ptr;
+
+	dst_ptr = (u32 *)dst;
+	src_ptr = (u32 *)src;
+
+	for (ui = 0; ui < len; ui++) {
+		*dst_ptr = *src_ptr;
+		dst_ptr++;
+		src_ptr++;
+	}
+
+	return MV_OK;
+}
+
+int ddr3_sdram_dqs_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
+			   u32 *new_locked_pup, u32 *pattern,
+			   u32 pattern_len, u32 sdram_offset, int write,
+			   int mask, u32 *mask_pattern,
+			   int special_compare)
+{
+	u32 uj, pup_groups;
+
+	if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
+		pup_groups = 2;
+	else
+		pup_groups = 1;
+
+	ddr3_reset_phy_read_fifo();
+
+	/* Check if need to write to sdram before read */
+	if (write == 1)
+		ddr3_dram_sram_burst((u32)pattern, sdram_offset, pattern_len);
+
+	ddr3_dram_sram_burst(sdram_offset, (u32)sdram_data, pattern_len);
+
+	/* Compare read result to write */
+	for (uj = 0; uj < pattern_len; uj++) {
+		if (special_compare && special_compare_pattern(uj))
+			continue;
+
+		if (dram_info->ddr_width > 16) {
+			compare_pattern_v1(uj, new_locked_pup, pattern,
+					   pup_groups, 1);
+		} else {
+			compare_pattern_v2(uj, new_locked_pup, pattern);
+		}
+	}
+
+	return MV_OK;
+}
+
+void ddr3_reset_phy_read_fifo(void)
+{
+	u32 reg;
+
+	/* reset read FIFO */
+	reg = reg_read(REG_DRAM_TRAINING_ADDR);
+	/* Start Auto Read Leveling procedure */
+	reg |= (1 << REG_DRAM_TRAINING_RL_OFFS);
+
+	/* 0x15B0 - Training Register */
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS) +
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS));
+
+	/* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	do {
+		reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+			(1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
+	} while (reg);	/* Wait for '0' */
+
+	reg = reg_read(REG_DRAM_TRAINING_ADDR);
+
+	/* Clear Auto Read Leveling procedure */
+	reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS);
+
+	/* 0x15B0 - Training Register */
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);
+}
diff --git a/drivers/ddr/marvell/axp/ddr3_spd.c b/drivers/ddr/marvell/axp/ddr3_spd.c
new file mode 100644
index 0000000..e2305d8
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_spd.c
@@ -0,0 +1,1299 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_init.h"
+
+#if defined(MV88F78X60)
+#include "ddr3_axp_config.h"
+#elif defined(MV88F67XX)
+#include "ddr3_a370_config.h"
+#endif
+
+#if defined(MV88F672X)
+#include "ddr3_a375_config.h"
+#endif
+
+#ifdef DUNIT_SPD
+
+/* DIMM SPD offsets */
+#define SPD_DEV_TYPE_BYTE		2
+
+#define SPD_MODULE_TYPE_BYTE		3
+#define SPD_MODULE_MASK			0xf
+#define SPD_MODULE_TYPE_RDIMM		1
+#define SPD_MODULE_TYPE_UDIMM		2
+
+#define SPD_DEV_DENSITY_BYTE		4
+#define SPD_DEV_DENSITY_MASK		0xf
+
+#define SPD_ROW_NUM_BYTE		5
+#define SPD_ROW_NUM_MIN			12
+#define SPD_ROW_NUM_OFF			3
+#define SPD_ROW_NUM_MASK		(7 << SPD_ROW_NUM_OFF)
+
+#define SPD_COL_NUM_BYTE		5
+#define SPD_COL_NUM_MIN			9
+#define SPD_COL_NUM_OFF			0
+#define SPD_COL_NUM_MASK		(7 << SPD_COL_NUM_OFF)
+
+#define SPD_MODULE_ORG_BYTE		7
+#define SPD_MODULE_SDRAM_DEV_WIDTH_OFF 	0
+#define SPD_MODULE_SDRAM_DEV_WIDTH_MASK	(7 << SPD_MODULE_SDRAM_DEV_WIDTH_OFF)
+#define SPD_MODULE_BANK_NUM_MIN		1
+#define SPD_MODULE_BANK_NUM_OFF		3
+#define SPD_MODULE_BANK_NUM_MASK	(7 << SPD_MODULE_BANK_NUM_OFF)
+
+#define SPD_BUS_WIDTH_BYTE		8
+#define SPD_BUS_WIDTH_OFF		0
+#define SPD_BUS_WIDTH_MASK		(7 << SPD_BUS_WIDTH_OFF)
+#define SPD_BUS_ECC_OFF			3
+#define SPD_BUS_ECC_MASK		(3 << SPD_BUS_ECC_OFF)
+
+#define SPD_MTB_DIVIDEND_BYTE		10
+#define SPD_MTB_DIVISOR_BYTE		11
+#define SPD_TCK_BYTE			12
+#define SPD_SUP_CAS_LAT_LSB_BYTE	14
+#define SPD_SUP_CAS_LAT_MSB_BYTE	15
+#define SPD_TAA_BYTE			16
+#define SPD_TWR_BYTE			17
+#define SPD_TRCD_BYTE			18
+#define SPD_TRRD_BYTE			19
+#define SPD_TRP_BYTE			20
+
+#define SPD_TRAS_MSB_BYTE		21
+#define SPD_TRAS_MSB_MASK		0xf
+
+#define SPD_TRC_MSB_BYTE		21
+#define SPD_TRC_MSB_MASK		0xf0
+
+#define SPD_TRAS_LSB_BYTE		22
+#define SPD_TRC_LSB_BYTE		23
+#define SPD_TRFC_LSB_BYTE		24
+#define SPD_TRFC_MSB_BYTE		25
+#define SPD_TWTR_BYTE			26
+#define SPD_TRTP_BYTE			27
+
+#define SPD_TFAW_MSB_BYTE		28
+#define SPD_TFAW_MSB_MASK		0xf
+
+#define SPD_TFAW_LSB_BYTE		29
+#define SPD_OPT_FEATURES_BYTE		30
+#define SPD_THERMAL_REFRESH_OPT_BYTE	31
+
+#define SPD_ADDR_MAP_BYTE		63
+#define SPD_ADDR_MAP_MIRROR_OFFS	0
+
+#define SPD_RDIMM_RC_BYTE		69
+#define SPD_RDIMM_RC_NIBBLE_MASK	0xF
+#define SPD_RDIMM_RC_NUM		16
+
+/* Dimm Memory Type values */
+#define SPD_MEM_TYPE_SDRAM		0x4
+#define SPD_MEM_TYPE_DDR1		0x7
+#define SPD_MEM_TYPE_DDR2		0x8
+#define SPD_MEM_TYPE_DDR3		0xB
+
+#define DIMM_MODULE_MANU_OFFS		64
+#define DIMM_MODULE_MANU_SIZE		8
+#define DIMM_MODULE_VEN_OFFS		73
+#define DIMM_MODULE_VEN_SIZE		25
+#define DIMM_MODULE_ID_OFFS		99
+#define DIMM_MODULE_ID_SIZE		18
+
+/* enumeration for voltage levels. */
+enum dimm_volt_if {
+	TTL_5V_TOLERANT,
+	LVTTL,
+	HSTL_1_5V,
+	SSTL_3_3V,
+	SSTL_2_5V,
+	VOLTAGE_UNKNOWN,
+};
+
+/* enumaration for SDRAM CAS Latencies. */
+enum dimm_sdram_cas {
+	SD_CL_1 = 1,
+	SD_CL_2,
+	SD_CL_3,
+	SD_CL_4,
+	SD_CL_5,
+	SD_CL_6,
+	SD_CL_7,
+	SD_FAULT
+};
+
+/* enumeration for memory types */
+enum memory_type {
+	MEM_TYPE_SDRAM,
+	MEM_TYPE_DDR1,
+	MEM_TYPE_DDR2,
+	MEM_TYPE_DDR3
+};
+
+/* DIMM information structure */
+typedef struct dimm_info {
+	/* DIMM dimensions */
+	u32 num_of_module_ranks;
+	u32 data_width;
+	u32 rank_capacity;
+	u32 num_of_devices;
+
+	u32 sdram_width;
+	u32 num_of_banks_on_each_device;
+	u32 sdram_capacity;
+
+	u32 num_of_row_addr;
+	u32 num_of_col_addr;
+
+	u32 addr_mirroring;
+
+	u32 err_check_type;			/* ECC , PARITY.. */
+	u32 type_info;				/* DDR2 only */
+
+	/* DIMM timing parameters */
+	u32 supported_cas_latencies;
+	u32 refresh_interval;
+	u32 min_cycle_time;
+	u32 min_row_precharge_time;
+	u32 min_row_active_to_row_active;
+	u32 min_ras_to_cas_delay;
+	u32 min_write_recovery_time;		/* DDR3/2 only */
+	u32 min_write_to_read_cmd_delay;	/* DDR3/2 only */
+	u32 min_read_to_prech_cmd_delay;	/* DDR3/2 only */
+	u32 min_active_to_precharge;
+	u32 min_refresh_recovery;		/* DDR3/2 only */
+	u32 min_cas_lat_time;
+	u32 min_four_active_win_delay;
+	u8 dimm_rc[SPD_RDIMM_RC_NUM];
+
+	/* DIMM vendor ID */
+	u32 vendor;
+} MV_DIMM_INFO;
+
+static int ddr3_spd_sum_init(MV_DIMM_INFO *info, MV_DIMM_INFO *sum_info,
+			     u32 dimm);
+static u32 ddr3_get_max_val(u32 spd_val, u32 dimm_num, u32 static_val);
+static u32 ddr3_get_min_val(u32 spd_val, u32 dimm_num, u32 static_val);
+static int ddr3_spd_init(MV_DIMM_INFO *info, u32 dimm_addr, u32 dimm_width);
+static u32 ddr3_div(u32 val, u32 divider, u32 sub);
+
+extern u8 spd_data[SPD_SIZE];
+extern u32 odt_config[ODT_OPT];
+extern u16 odt_static[ODT_OPT][MAX_CS];
+extern u16 odt_dynamic[ODT_OPT][MAX_CS];
+
+#if !(defined(DB_88F6710) || defined(DB_88F6710_PCAC) || defined(RD_88F6710))
+/*
+ * Name:     ddr3_get_dimm_num - Find number of dimms and their addresses
+ * Desc:
+ * Args:     dimm_addr - array of dimm addresses
+ * Notes:
+ * Returns:  None.
+ */
+static u32 ddr3_get_dimm_num(u32 *dimm_addr)
+{
+	u32 dimm_cur_addr;
+	u8 data[3];
+	u32 dimm_num = 0;
+	int ret;
+
+	/* Read the dimm eeprom */
+	for (dimm_cur_addr = MAX_DIMM_ADDR; dimm_cur_addr > MIN_DIMM_ADDR;
+	     dimm_cur_addr--) {
+		data[SPD_DEV_TYPE_BYTE] = 0;
+
+		/* Far-End DIMM must be connected */
+		if ((dimm_num == 0) && (dimm_cur_addr < FAR_END_DIMM_ADDR))
+			return 0;
+
+		ret = i2c_read(dimm_cur_addr, 0, 1, (uchar *)data, 3);
+		if (!ret) {
+			if (data[SPD_DEV_TYPE_BYTE] == SPD_MEM_TYPE_DDR3) {
+				dimm_addr[dimm_num] = dimm_cur_addr;
+				dimm_num++;
+			}
+		}
+	}
+
+	return dimm_num;
+}
+#endif
+
+/*
+ * Name:     dimmSpdInit - Get the SPD parameters.
+ * Desc:     Read the DIMM SPD parameters into given struct parameter.
+ * Args:     dimmNum - DIMM number. See MV_BOARD_DIMM_NUM enumerator.
+ *           info - DIMM information structure.
+ * Notes:
+ * Returns:  MV_OK if function could read DIMM parameters, 0 otherwise.
+ */
+int ddr3_spd_init(MV_DIMM_INFO *info, u32 dimm_addr, u32 dimm_width)
+{
+	u32 tmp;
+	u32 time_base;
+	int ret;
+	__maybe_unused u32 rc;
+	__maybe_unused u8 vendor_high, vendor_low;
+
+	if (dimm_addr != 0) {
+		memset(spd_data, 0, SPD_SIZE * sizeof(u8));
+
+		ret = i2c_read(dimm_addr, 0, 1, (uchar *)spd_data, SPD_SIZE);
+		if (ret)
+			return MV_DDR3_TRAINING_ERR_TWSI_FAIL;
+	}
+
+	/* Check if DDR3 */
+	if (spd_data[SPD_DEV_TYPE_BYTE] != SPD_MEM_TYPE_DDR3)
+		return MV_DDR3_TRAINING_ERR_TWSI_BAD_TYPE;
+
+	/* Error Check Type */
+	/* No byte for error check in DDR3 SPD, use DDR2 convention */
+	info->err_check_type = 0;
+
+	/* Check if ECC */
+	if ((spd_data[SPD_BUS_WIDTH_BYTE] & 0x18) >> 3)
+		info->err_check_type = 1;
+
+	DEBUG_INIT_FULL_C("DRAM err_check_type ", info->err_check_type, 1);
+	switch (spd_data[SPD_MODULE_TYPE_BYTE]) {
+	case 1:
+		/* support RDIMM */
+		info->type_info = SPD_MODULE_TYPE_RDIMM;
+		break;
+	case 2:
+		/* support UDIMM */
+		info->type_info = SPD_MODULE_TYPE_UDIMM;
+		break;
+	case 11:		/* LRDIMM current not supported */
+	default:
+		info->type_info = (spd_data[SPD_MODULE_TYPE_BYTE]);
+		break;
+	}
+
+	/* Size Calculations: */
+
+	/* Number Of Row Addresses - 12/13/14/15/16 */
+	info->num_of_row_addr =
+		(spd_data[SPD_ROW_NUM_BYTE] & SPD_ROW_NUM_MASK) >>
+		SPD_ROW_NUM_OFF;
+	info->num_of_row_addr += SPD_ROW_NUM_MIN;
+	DEBUG_INIT_FULL_C("DRAM num_of_row_addr ", info->num_of_row_addr, 2);
+
+	/* Number Of Column Addresses - 9/10/11/12 */
+	info->num_of_col_addr =
+		(spd_data[SPD_COL_NUM_BYTE] & SPD_COL_NUM_MASK) >>
+		SPD_COL_NUM_OFF;
+	info->num_of_col_addr += SPD_COL_NUM_MIN;
+	DEBUG_INIT_FULL_C("DRAM num_of_col_addr ", info->num_of_col_addr, 1);
+
+	/* Number Of Ranks = number of CS on Dimm - 1/2/3/4 Ranks */
+	info->num_of_module_ranks =
+		(spd_data[SPD_MODULE_ORG_BYTE] & SPD_MODULE_BANK_NUM_MASK) >>
+		SPD_MODULE_BANK_NUM_OFF;
+	info->num_of_module_ranks += SPD_MODULE_BANK_NUM_MIN;
+	DEBUG_INIT_FULL_C("DRAM numOfModuleBanks ", info->num_of_module_ranks,
+			  1);
+
+	/* Data Width - 8/16/32/64 bits */
+	info->data_width =
+		1 << (3 + (spd_data[SPD_BUS_WIDTH_BYTE] & SPD_BUS_WIDTH_MASK));
+	DEBUG_INIT_FULL_C("DRAM data_width ", info->data_width, 1);
+
+	/* Number Of Banks On Each Device - 8/16/32/64 banks */
+	info->num_of_banks_on_each_device =
+		1 << (3 + ((spd_data[SPD_DEV_DENSITY_BYTE] >> 4) & 0x7));
+	DEBUG_INIT_FULL_C("DRAM num_of_banks_on_each_device ",
+			  info->num_of_banks_on_each_device, 1);
+
+	/* Total SDRAM capacity - 256Mb/512Mb/1Gb/2Gb/4Gb/8Gb/16Gb - MegaBits */
+	info->sdram_capacity =
+		spd_data[SPD_DEV_DENSITY_BYTE] & SPD_DEV_DENSITY_MASK;
+
+	/* Sdram Width - 4/8/16/32 bits */
+	info->sdram_width = 1 << (2 + (spd_data[SPD_MODULE_ORG_BYTE] &
+				       SPD_MODULE_SDRAM_DEV_WIDTH_MASK));
+	DEBUG_INIT_FULL_C("DRAM sdram_width ", info->sdram_width, 1);
+
+	/* CS (Rank) Capacity - MB */
+	/*
+	 * DDR3 device uiDensity val are: (device capacity/8) *
+	 * (Module_width/Device_width)
+	 */
+	/* Jedec SPD DDR3 - page 7, Save spd_data in Mb  - 2048=2GB */
+	if (dimm_width == 32) {
+		info->rank_capacity =
+			((1 << info->sdram_capacity) * 256 *
+			 (info->data_width / info->sdram_width)) << 16;
+		/* CS size = CS size / 2  */
+	} else {
+		info->rank_capacity =
+			((1 << info->sdram_capacity) * 256 *
+			 (info->data_width / info->sdram_width) * 0x2) << 16;
+		/* 0x2 =>  0x100000-1Mbit / 8-bit->byte / 0x10000  */
+	}
+	DEBUG_INIT_FULL_C("DRAM rank_capacity[31] ", info->rank_capacity, 1);
+
+	/* Number of devices includeing Error correction */
+	info->num_of_devices =
+		((info->data_width / info->sdram_width) *
+		 info->num_of_module_ranks) + info->err_check_type;
+	DEBUG_INIT_FULL_C("DRAM num_of_devices  ", info->num_of_devices, 1);
+
+	/* Address Mapping from Edge connector to DRAM - mirroring option */
+	info->addr_mirroring =
+		spd_data[SPD_ADDR_MAP_BYTE] & (1 << SPD_ADDR_MAP_MIRROR_OFFS);
+
+	/* Timings - All in ps */
+
+	time_base = (1000 * spd_data[SPD_MTB_DIVIDEND_BYTE]) /
+		spd_data[SPD_MTB_DIVISOR_BYTE];
+
+	/* Minimum Cycle Time At Max CasLatancy */
+	info->min_cycle_time = spd_data[SPD_TCK_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM tCKmin ", info->min_cycle_time, 1);
+
+	/* Refresh Interval */
+	/* No byte for refresh interval in DDR3 SPD, use DDR2 convention */
+	/*
+	 * JEDEC param are 0 <= Tcase <= 85: 7.8uSec, 85 <= Tcase
+	 * <= 95: 3.9uSec
+	 */
+	info->refresh_interval = 7800000;	/* Set to 7.8uSec */
+	DEBUG_INIT_FULL_C("DRAM refresh_interval ", info->refresh_interval, 1);
+
+	/* Suported Cas Latencies -  DDR 3: */
+
+	/*
+	 *         bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 *
+	 *******-******-******-******-******-******-******-*******-*******
+	 CAS =      11  |  10  |  9   |  8   |  7   |  6   |  5   |  4   *
+	 *********************************************************-*******
+	 *******-******-******-******-******-******-******-*******-*******
+	 *        bit15 |bit14 |bit13 |bit12 |bit11 |bit10 | bit9 | bit8 *
+	 *******-******-******-******-******-******-******-*******-*******
+	 CAS =     TBD  |  18  |  17  |  16  |  15  |  14  |  13  |  12  *
+	*/
+
+	/* DDR3 include 2 byte of CAS support */
+	info->supported_cas_latencies =
+		(spd_data[SPD_SUP_CAS_LAT_MSB_BYTE] << 8) |
+		spd_data[SPD_SUP_CAS_LAT_LSB_BYTE];
+	DEBUG_INIT_FULL_C("DRAM supported_cas_latencies ",
+			  info->supported_cas_latencies, 1);
+
+	/* Minimum Cycle Time At Max CasLatancy */
+	info->min_cas_lat_time = (spd_data[SPD_TAA_BYTE] * time_base);
+	/*
+	 * This field divided by the cycleTime will give us the CAS latency
+	 * to config
+	 */
+
+	/*
+	 * For DDR3 and DDR2 includes Write Recovery Time field.
+	 * Other SDRAM ignore
+	 */
+	info->min_write_recovery_time = spd_data[SPD_TWR_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_write_recovery_time ",
+			  info->min_write_recovery_time, 1);
+
+	/* Mininmum Ras to Cas Delay */
+	info->min_ras_to_cas_delay = spd_data[SPD_TRCD_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_ras_to_cas_delay ",
+			  info->min_ras_to_cas_delay, 1);
+
+	/* Minimum Row Active to Row Active Time */
+	info->min_row_active_to_row_active =
+	    spd_data[SPD_TRRD_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_row_active_to_row_active ",
+			  info->min_row_active_to_row_active, 1);
+
+	/* Minimum Row Precharge Delay Time */
+	info->min_row_precharge_time = spd_data[SPD_TRP_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_row_precharge_time ",
+			  info->min_row_precharge_time, 1);
+
+	/* Minimum Active to Precharge Delay Time - tRAS   ps */
+	info->min_active_to_precharge =
+		(spd_data[SPD_TRAS_MSB_BYTE] & SPD_TRAS_MSB_MASK) << 8;
+	info->min_active_to_precharge |= spd_data[SPD_TRAS_LSB_BYTE];
+	info->min_active_to_precharge *= time_base;
+	DEBUG_INIT_FULL_C("DRAM min_active_to_precharge ",
+			  info->min_active_to_precharge, 1);
+
+	/* Minimum Refresh Recovery Delay Time - tRFC  ps */
+	info->min_refresh_recovery = spd_data[SPD_TRFC_MSB_BYTE] << 8;
+	info->min_refresh_recovery |= spd_data[SPD_TRFC_LSB_BYTE];
+	info->min_refresh_recovery *= time_base;
+	DEBUG_INIT_FULL_C("DRAM min_refresh_recovery ",
+			  info->min_refresh_recovery, 1);
+
+	/*
+	 * For DDR3 and DDR2 includes Internal Write To Read Command Delay
+	 * field.
+	 */
+	info->min_write_to_read_cmd_delay = spd_data[SPD_TWTR_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_write_to_read_cmd_delay ",
+			  info->min_write_to_read_cmd_delay, 1);
+
+	/*
+	 * For DDR3 and DDR2 includes Internal Read To Precharge Command Delay
+	 * field.
+	 */
+	info->min_read_to_prech_cmd_delay = spd_data[SPD_TRTP_BYTE] * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_read_to_prech_cmd_delay ",
+			  info->min_read_to_prech_cmd_delay, 1);
+
+	/*
+	 * For DDR3 includes Minimum Activate to Activate/Refresh Command
+	 * field
+	 */
+	tmp = ((spd_data[SPD_TFAW_MSB_BYTE] & SPD_TFAW_MSB_MASK) << 8) |
+		spd_data[SPD_TFAW_LSB_BYTE];
+	info->min_four_active_win_delay = tmp * time_base;
+	DEBUG_INIT_FULL_C("DRAM min_four_active_win_delay ",
+			  info->min_four_active_win_delay, 1);
+
+#if defined(MV88F78X60) || defined(MV88F672X)
+	/* Registered DIMM support */
+	if (info->type_info == SPD_MODULE_TYPE_RDIMM) {
+		for (rc = 2; rc < 6; rc += 2) {
+			tmp = spd_data[SPD_RDIMM_RC_BYTE + rc / 2];
+			info->dimm_rc[rc] =
+				spd_data[SPD_RDIMM_RC_BYTE + rc / 2] &
+				SPD_RDIMM_RC_NIBBLE_MASK;
+			info->dimm_rc[rc + 1] =
+				(spd_data[SPD_RDIMM_RC_BYTE + rc / 2] >> 4) &
+				SPD_RDIMM_RC_NIBBLE_MASK;
+		}
+
+		vendor_low = spd_data[66];
+		vendor_high = spd_data[65];
+		info->vendor = (vendor_high << 8) + vendor_low;
+		DEBUG_INIT_C("DDR3 Training Sequence - Registered DIMM vendor ID 0x",
+			     info->vendor, 4);
+
+		info->dimm_rc[0] = RDIMM_RC0;
+		info->dimm_rc[1] = RDIMM_RC1;
+		info->dimm_rc[2] = RDIMM_RC2;
+		info->dimm_rc[8] = RDIMM_RC8;
+		info->dimm_rc[9] = RDIMM_RC9;
+		info->dimm_rc[10] = RDIMM_RC10;
+		info->dimm_rc[11] = RDIMM_RC11;
+	}
+#endif
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_spd_sum_init - Get the SPD parameters.
+ * Desc:     Read the DIMM SPD parameters into given struct parameter.
+ * Args:     dimmNum - DIMM number. See MV_BOARD_DIMM_NUM enumerator.
+ *           info - DIMM information structure.
+ * Notes:
+ * Returns:  MV_OK if function could read DIMM parameters, 0 otherwise.
+ */
+int ddr3_spd_sum_init(MV_DIMM_INFO *info, MV_DIMM_INFO *sum_info, u32 dimm)
+{
+	if (dimm == 0) {
+		memcpy(sum_info, info, sizeof(MV_DIMM_INFO));
+		return MV_OK;
+	}
+	if (sum_info->type_info != info->type_info) {
+		DEBUG_INIT_S("DDR3 Dimm Compare - DIMM type does not match - FAIL\n");
+		return MV_DDR3_TRAINING_ERR_DIMM_TYPE_NO_MATCH;
+	}
+	if (sum_info->err_check_type > info->err_check_type) {
+		sum_info->err_check_type = info->err_check_type;
+		DEBUG_INIT_S("DDR3 Dimm Compare - ECC does not match. ECC is disabled\n");
+	}
+	if (sum_info->data_width != info->data_width) {
+		DEBUG_INIT_S("DDR3 Dimm Compare - DRAM bus width does not match - FAIL\n");
+		return MV_DDR3_TRAINING_ERR_BUS_WIDTH_NOT_MATCH;
+	}
+	if (sum_info->min_cycle_time < info->min_cycle_time)
+		sum_info->min_cycle_time = info->min_cycle_time;
+	if (sum_info->refresh_interval < info->refresh_interval)
+		sum_info->refresh_interval = info->refresh_interval;
+	sum_info->supported_cas_latencies &= info->supported_cas_latencies;
+	if (sum_info->min_cas_lat_time < info->min_cas_lat_time)
+		sum_info->min_cas_lat_time = info->min_cas_lat_time;
+	if (sum_info->min_write_recovery_time < info->min_write_recovery_time)
+		sum_info->min_write_recovery_time =
+		    info->min_write_recovery_time;
+	if (sum_info->min_ras_to_cas_delay < info->min_ras_to_cas_delay)
+		sum_info->min_ras_to_cas_delay = info->min_ras_to_cas_delay;
+	if (sum_info->min_row_active_to_row_active <
+	    info->min_row_active_to_row_active)
+		sum_info->min_row_active_to_row_active =
+		    info->min_row_active_to_row_active;
+	if (sum_info->min_row_precharge_time < info->min_row_precharge_time)
+		sum_info->min_row_precharge_time = info->min_row_precharge_time;
+	if (sum_info->min_active_to_precharge < info->min_active_to_precharge)
+		sum_info->min_active_to_precharge =
+		    info->min_active_to_precharge;
+	if (sum_info->min_refresh_recovery < info->min_refresh_recovery)
+		sum_info->min_refresh_recovery = info->min_refresh_recovery;
+	if (sum_info->min_write_to_read_cmd_delay <
+	    info->min_write_to_read_cmd_delay)
+		sum_info->min_write_to_read_cmd_delay =
+		    info->min_write_to_read_cmd_delay;
+	if (sum_info->min_read_to_prech_cmd_delay <
+	    info->min_read_to_prech_cmd_delay)
+		sum_info->min_read_to_prech_cmd_delay =
+		    info->min_read_to_prech_cmd_delay;
+	if (sum_info->min_four_active_win_delay <
+	    info->min_four_active_win_delay)
+		sum_info->min_four_active_win_delay =
+		    info->min_four_active_win_delay;
+	if (sum_info->min_write_to_read_cmd_delay <
+	    info->min_write_to_read_cmd_delay)
+		sum_info->min_write_to_read_cmd_delay =
+			info->min_write_to_read_cmd_delay;
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_dunit_setup
+ * Desc:     Set the controller with the timing values.
+ * Args:     ecc_ena - User ECC setup
+ * Notes:
+ * Returns:
+ */
+int ddr3_dunit_setup(u32 ecc_ena, u32 hclk_time, u32 *ddr_width)
+{
+	u32 reg, tmp, cwl;
+	u32 ddr_clk_time;
+	MV_DIMM_INFO dimm_info[2];
+	MV_DIMM_INFO sum_info;
+	u32 stat_val, spd_val;
+	u32 cs, cl, cs_num, cs_ena;
+	u32 dimm_num = 0;
+	int status;
+	u32 rc;
+	__maybe_unused u32 dimm_cnt, cs_count, dimm;
+	__maybe_unused u32 dimm_addr[2] = { 0, 0 };
+
+#if defined(DB_88F6710) || defined(DB_88F6710_PCAC) || defined(RD_88F6710)
+	/* Armada 370 - SPD is not available on DIMM */
+	/*
+	 * Set MC registers according to Static SPD values Values -
+	 * must be set manually
+	 */
+	/*
+	 * We only have one optional DIMM for the DB and we already got the
+	 * SPD matching values
+	 */
+	status = ddr3_spd_init(&dimm_info[0], 0, *ddr_width);
+	if (MV_OK != status)
+		return status;
+
+	dimm_num = 1;
+	/* Use JP8 to enable multiCS support for Armada 370 DB */
+	if (!ddr3_check_config(EEPROM_MODULE_ADDR, CONFIG_MULTI_CS))
+		dimm_info[0].num_of_module_ranks = 1;
+	status = ddr3_spd_sum_init(&dimm_info[0], &sum_info, 0);
+	if (MV_OK != status)
+		return status;
+#else
+	/* Dynamic D-Unit Setup - Read SPD values */
+#ifdef DUNIT_SPD
+	dimm_num = ddr3_get_dimm_num(dimm_addr);
+	if (dimm_num == 0) {
+#ifdef MIXED_DIMM_STATIC
+		DEBUG_INIT_S("DDR3 Training Sequence - No DIMMs detected\n");
+#else
+		DEBUG_INIT_S("DDR3 Training Sequence - FAILED (Wrong DIMMs Setup)\n");
+		return MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP;
+#endif
+	} else {
+		DEBUG_INIT_C("DDR3 Training Sequence - Number of DIMMs detected: ",
+			     dimm_num, 1);
+	}
+
+	for (dimm = 0; dimm < dimm_num; dimm++) {
+		status = ddr3_spd_init(&dimm_info[dimm], dimm_addr[dimm],
+				       *ddr_width);
+		if (MV_OK != status)
+			return status;
+		status = ddr3_spd_sum_init(&dimm_info[dimm], &sum_info, dimm);
+		if (MV_OK != status)
+			return status;
+	}
+#endif
+#endif
+
+	/* Set number of enabled CS */
+	cs_num = 0;
+#ifdef DUNIT_STATIC
+	cs_num = ddr3_get_cs_num_from_reg();
+#endif
+#ifdef DUNIT_SPD
+	for (dimm = 0; dimm < dimm_num; dimm++)
+		cs_num += dimm_info[dimm].num_of_module_ranks;
+#endif
+	if (cs_num > MAX_CS) {
+		DEBUG_INIT_C("DDR3 Training Sequence - Number of CS exceed limit -  ",
+			     MAX_CS, 1);
+		return MV_DDR3_TRAINING_ERR_MAX_CS_LIMIT;
+	}
+
+	/* Set bitmap of enabled CS */
+	cs_ena = 0;
+#ifdef DUNIT_STATIC
+	cs_ena = ddr3_get_cs_ena_from_reg();
+#endif
+#ifdef DUNIT_SPD
+	dimm = 0;
+
+	if (dimm_num) {
+		for (cs = 0; cs < MAX_CS; cs += 2) {
+			if (((1 << cs) & DIMM_CS_BITMAP) &&
+			    !(cs_ena & (1 << cs))) {
+				if (dimm_info[dimm].num_of_module_ranks == 1)
+					cs_ena |= (0x1 << cs);
+				else if (dimm_info[dimm].num_of_module_ranks == 2)
+					cs_ena |= (0x3 << cs);
+				else if (dimm_info[dimm].num_of_module_ranks == 3)
+					cs_ena |= (0x7 << cs);
+				else if (dimm_info[dimm].num_of_module_ranks == 4)
+					cs_ena |= (0xF << cs);
+
+				dimm++;
+				if (dimm == dimm_num)
+					break;
+			}
+		}
+	}
+#endif
+
+	if (cs_ena > 0xF) {
+		DEBUG_INIT_C("DDR3 Training Sequence - Number of enabled CS exceed limit -  ",
+			     MAX_CS, 1);
+		return MV_DDR3_TRAINING_ERR_MAX_ENA_CS_LIMIT;
+	}
+
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - Number of CS = ", cs_num, 1);
+
+	/* Check Ratio - '1' - 2:1, '0' - 1:1 */
+	if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS))
+		ddr_clk_time = hclk_time / 2;
+	else
+		ddr_clk_time = hclk_time;
+
+#ifdef DUNIT_STATIC
+	/* Get target CL value from set register */
+	reg = (reg_read(REG_DDR3_MR0_ADDR) >> 2);
+	reg = ((((reg >> 1) & 0xE)) | (reg & 0x1)) & 0xF;
+
+	cl = ddr3_get_max_val(ddr3_div(sum_info.min_cas_lat_time,
+				       ddr_clk_time, 0),
+			      dimm_num, ddr3_valid_cl_to_cl(reg));
+#else
+	cl = ddr3_div(sum_info.min_cas_lat_time, ddr_clk_time, 0);
+#endif
+	if (cl < 5)
+		cl = 5;
+
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - Cas Latency = ", cl, 1);
+
+	/* {0x00001400} -   DDR SDRAM Configuration Register */
+	reg = 0x73004000;
+	stat_val = ddr3_get_static_mc_value(
+		REG_SDRAM_CONFIG_ADDR, REG_SDRAM_CONFIG_ECC_OFFS, 0x1, 0, 0);
+	if (ecc_ena && ddr3_get_min_val(sum_info.err_check_type, dimm_num,
+					stat_val)) {
+		reg |= (1 << REG_SDRAM_CONFIG_ECC_OFFS);
+		reg |= (1 << REG_SDRAM_CONFIG_IERR_OFFS);
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - ECC Enabled\n");
+	} else {
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - ECC Disabled\n");
+	}
+
+	if (sum_info.type_info == SPD_MODULE_TYPE_RDIMM) {
+#ifdef DUNIT_STATIC
+		DEBUG_INIT_S("DDR3 Training Sequence - FAIL - Illegal R-DIMM setup\n");
+		return MV_DDR3_TRAINING_ERR_BAD_R_DIMM_SETUP;
+#endif
+		reg |= (1 << REG_SDRAM_CONFIG_REGDIMM_OFFS);
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - R-DIMM\n");
+	} else {
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - U-DIMM\n");
+	}
+
+#ifndef MV88F67XX
+#ifdef DUNIT_STATIC
+	if (ddr3_get_min_val(sum_info.data_width, dimm_num, BUS_WIDTH) == 64) {
+#else
+	if (*ddr_width == 64) {
+#endif
+		reg |= (1 << REG_SDRAM_CONFIG_WIDTH_OFFS);
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 64Bits\n");
+	} else {
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 32Bits\n");
+	}
+#else
+	DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 16Bits\n");
+#endif
+
+#if defined(MV88F672X)
+	if (*ddr_width == 32) {
+		reg |= (1 << REG_SDRAM_CONFIG_WIDTH_OFFS);
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 32Bits\n");
+	} else {
+		DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 16Bits\n");
+	}
+#endif
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_CONFIG_ADDR, 0,
+					       REG_SDRAM_CONFIG_RFRS_MASK, 0, 0);
+	tmp = ddr3_get_min_val(sum_info.refresh_interval / hclk_time,
+			       dimm_num, stat_val);
+
+#ifdef TREFI_USER_EN
+	tmp = min(TREFI_USER / hclk_time, tmp);
+#endif
+
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - RefreshInterval/Hclk = ", tmp, 4);
+	reg |= tmp;
+
+	if (cl != 3)
+		reg |= (1 << 16);	/*  If 2:1 need to set P2DWr */
+
+#if defined(MV88F672X)
+	reg |= (1 << 27);	/* PhyRfRST = Disable */
+#endif
+	reg_write(REG_SDRAM_CONFIG_ADDR, reg);
+
+	/*{0x00001404}  -   DDR SDRAM Configuration Register */
+	reg = 0x3630B800;
+#ifdef DUNIT_SPD
+	reg |= (DRAM_2T << REG_DUNIT_CTRL_LOW_2T_OFFS);
+#endif
+	reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+
+	/* {0x00001408}  -   DDR SDRAM Timing (Low) Register */
+	reg = 0x0;
+
+	/* tRAS - (0:3,20) */
+	spd_val = ddr3_div(sum_info.min_active_to_precharge,
+			    ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    0, 0xF, 16, 0x10);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRAS-1 = ", tmp, 1);
+	reg |= (tmp & 0xF);
+	reg |= ((tmp & 0x10) << 16);	/* to bit 20 */
+
+	/* tRCD - (4:7) */
+	spd_val = ddr3_div(sum_info.min_ras_to_cas_delay, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    4, 0xF, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRCD-1 = ", tmp, 1);
+	reg |= ((tmp & 0xF) << 4);
+
+	/* tRP - (8:11) */
+	spd_val = ddr3_div(sum_info.min_row_precharge_time, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    8, 0xF, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRP-1 = ", tmp, 1);
+	reg |= ((tmp & 0xF) << 8);
+
+	/* tWR - (12:15) */
+	spd_val = ddr3_div(sum_info.min_write_recovery_time, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    12, 0xF, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tWR-1 = ", tmp, 1);
+	reg |= ((tmp & 0xF) << 12);
+
+	/* tWTR - (16:19) */
+	spd_val = ddr3_div(sum_info.min_write_to_read_cmd_delay, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    16, 0xF, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tWTR-1 = ", tmp, 1);
+	reg |= ((tmp & 0xF) << 16);
+
+	/* tRRD - (24:27) */
+	spd_val = ddr3_div(sum_info.min_row_active_to_row_active, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    24, 0xF, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRRD-1 = ", tmp, 1);
+	reg |= ((tmp & 0xF) << 24);
+
+	/* tRTP - (28:31) */
+	spd_val = ddr3_div(sum_info.min_read_to_prech_cmd_delay, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR,
+					    28, 0xF, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRTP-1 = ", tmp, 1);
+	reg |= ((tmp & 0xF) << 28);
+
+	if (cl < 7)
+		reg = 0x33137663;
+
+	reg_write(REG_SDRAM_TIMING_LOW_ADDR, reg);
+
+	/*{0x0000140C}  -   DDR SDRAM Timing (High) Register */
+	/* Add cycles to R2R W2W */
+	reg = 0x39F8FF80;
+
+	/* tRFC - (0:6,16:18) */
+	spd_val = ddr3_div(sum_info.min_refresh_recovery, ddr_clk_time, 1);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_HIGH_ADDR,
+					    0, 0x7F, 9, 0x380);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRFC-1 = ", tmp, 1);
+	reg |= (tmp & 0x7F);
+	reg |= ((tmp & 0x380) << 9);	/* to bit 16 */
+	reg_write(REG_SDRAM_TIMING_HIGH_ADDR, reg);
+
+	/*{0x00001410}  -   DDR SDRAM Address Control Register */
+	reg = 0x000F0000;
+
+	/* tFAW - (24:28)  */
+#if (defined(MV88F78X60) || defined(MV88F672X))
+	tmp = sum_info.min_four_active_win_delay;
+	spd_val = ddr3_div(tmp, ddr_clk_time, 0);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_ADDRESS_CTRL_ADDR,
+					    24, 0x3F, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tFAW = ", tmp, 1);
+	reg |= ((tmp & 0x3F) << 24);
+#else
+	tmp = sum_info.min_four_active_win_delay -
+		4 * (sum_info.min_row_active_to_row_active);
+	spd_val = ddr3_div(tmp, ddr_clk_time, 0);
+	stat_val = ddr3_get_static_mc_value(REG_SDRAM_ADDRESS_CTRL_ADDR,
+					    24, 0x1F, 0, 0);
+	tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val);
+	DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tFAW-4*tRRD = ", tmp, 1);
+	reg |= ((tmp & 0x1F) << 24);
+#endif
+
+	/* SDRAM device capacity */
+#ifdef DUNIT_STATIC
+	reg |= (reg_read(REG_SDRAM_ADDRESS_CTRL_ADDR) & 0xF0FFFF);
+#endif
+
+#ifdef DUNIT_SPD
+	cs_count = 0;
+	dimm_cnt = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs) & DIMM_CS_BITMAP) {
+			if (dimm_info[dimm_cnt].num_of_module_ranks == cs_count) {
+				dimm_cnt++;
+				cs_count = 0;
+			}
+			cs_count++;
+			if (dimm_info[dimm_cnt].sdram_capacity < 0x3) {
+				reg |= ((dimm_info[dimm_cnt].sdram_capacity + 1) <<
+					(REG_SDRAM_ADDRESS_SIZE_OFFS +
+					 (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs)));
+			} else if (dimm_info[dimm_cnt].sdram_capacity > 0x3) {
+				reg |= ((dimm_info[dimm_cnt].sdram_capacity & 0x3) <<
+					(REG_SDRAM_ADDRESS_SIZE_OFFS +
+					 (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs)));
+				reg |= ((dimm_info[dimm_cnt].sdram_capacity & 0x4) <<
+					(REG_SDRAM_ADDRESS_SIZE_HIGH_OFFS + cs));
+			}
+		}
+	}
+
+	/* SDRAM device structure */
+	cs_count = 0;
+	dimm_cnt = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs) & DIMM_CS_BITMAP) {
+			if (dimm_info[dimm_cnt].num_of_module_ranks == cs_count) {
+				dimm_cnt++;
+				cs_count = 0;
+			}
+			cs_count++;
+			if (dimm_info[dimm_cnt].sdram_width == 16)
+				reg |= (1 << (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs));
+		}
+	}
+#endif
+	reg_write(REG_SDRAM_ADDRESS_CTRL_ADDR, reg);
+
+	/*{0x00001418}  -   DDR SDRAM Operation Register */
+	reg = 0xF00;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs))
+			reg &= ~(1 << (cs + REG_SDRAM_OPERATION_CS_OFFS));
+	}
+	reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+	/*{0x00001420}  -   DDR SDRAM Extended Mode Register */
+	reg = 0x00000004;
+	reg_write(REG_SDRAM_EXT_MODE_ADDR, reg);
+
+	/*{0x00001424}  -   DDR Controller Control (High) Register */
+#if (defined(MV88F78X60) || defined(MV88F672X))
+	reg = 0x0000D3FF;
+#else
+	reg = 0x0100D1FF;
+#endif
+	reg_write(REG_DDR_CONT_HIGH_ADDR, reg);
+
+	/*{0x0000142C}  -   DDR3 Timing Register */
+	reg = 0x014C2F38;
+#if defined(MV88F78X60) || defined(MV88F672X)
+	reg = 0x1FEC2F38;
+#endif
+	reg_write(0x142C, reg);
+
+	/*{0x00001484}  - MBus CPU Block Register */
+#ifdef MV88F67XX
+	if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS))
+		reg_write(REG_MBUS_CPU_BLOCK_ADDR, 0x0000E907);
+#endif
+
+	/*
+	 * In case of mixed dimm and on-board devices setup paramters will
+	 * be taken statically
+	 */
+	/*{0x00001494}  -   DDR SDRAM ODT Control (Low) Register */
+	reg = odt_config[cs_ena];
+	reg_write(REG_SDRAM_ODT_CTRL_LOW_ADDR, reg);
+
+	/*{0x00001498}  -   DDR SDRAM ODT Control (High) Register */
+	reg = 0x00000000;
+	reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, reg);
+
+	/*{0x0000149C}  -   DDR Dunit ODT Control Register */
+	reg = cs_ena;
+	reg_write(REG_DUNIT_ODT_CTRL_ADDR, reg);
+
+	/*{0x000014A0}  -   DDR Dunit ODT Control Register */
+#if defined(MV88F672X)
+	reg = 0x000006A9;
+	reg_write(REG_DRAM_FIFO_CTRL_ADDR, reg);
+#endif
+
+	/*{0x000014C0}  -   DRAM address and Control Driving Strenght */
+	reg_write(REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR, 0x192435e9);
+
+	/*{0x000014C4}  -   DRAM Data and DQS Driving Strenght */
+	reg_write(REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR, 0xB2C35E9);
+
+#if (defined(MV88F78X60) || defined(MV88F672X))
+	/*{0x000014CC}  -   DRAM Main Pads Calibration Machine Control Register */
+	reg = reg_read(REG_DRAM_MAIN_PADS_CAL_ADDR);
+	reg_write(REG_DRAM_MAIN_PADS_CAL_ADDR, reg | (1 << 0));
+#endif
+
+#if defined(MV88F672X)
+	/* DRAM Main Pads Calibration Machine Control Register */
+	/* 0x14CC[4:3] - CalUpdateControl = IntOnly */
+	reg = reg_read(REG_DRAM_MAIN_PADS_CAL_ADDR);
+	reg &= 0xFFFFFFE7;
+	reg |= (1 << 3);
+	reg_write(REG_DRAM_MAIN_PADS_CAL_ADDR, reg);
+#endif
+
+#ifdef DUNIT_SPD
+	cs_count = 0;
+	dimm_cnt = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if ((1 << cs) & DIMM_CS_BITMAP) {
+			if ((1 << cs) & cs_ena) {
+				if (dimm_info[dimm_cnt].num_of_module_ranks ==
+				    cs_count) {
+					dimm_cnt++;
+					cs_count = 0;
+				}
+				cs_count++;
+				reg_write(REG_CS_SIZE_SCRATCH_ADDR + (cs * 0x8),
+					  dimm_info[dimm_cnt].rank_capacity - 1);
+			} else {
+				reg_write(REG_CS_SIZE_SCRATCH_ADDR + (cs * 0x8), 0);
+			}
+		}
+	}
+#endif
+
+	/*{0x00020184}  -   Close FastPath - 2G */
+	reg_write(REG_FASTPATH_WIN_0_CTRL_ADDR, 0);
+
+	/*{0x00001538}  -    Read Data Sample Delays Register */
+	reg = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs))
+			reg |= (cl << (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs));
+	}
+
+	reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg);
+	DEBUG_INIT_FULL_C("DDR3 - SPD-SET - Read Data Sample Delays = ", reg,
+			  1);
+
+	/*{0x0000153C}  -   Read Data Ready Delay Register */
+	reg = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg |= ((cl + 2) <<
+				(REG_READ_DATA_READY_DELAYS_OFFS * cs));
+		}
+	}
+	reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg);
+	DEBUG_INIT_FULL_C("DDR3 - SPD-SET - Read Data Ready Delays = ", reg, 1);
+
+	/* Set MR registers */
+	/* MR0 */
+	reg = 0x00000600;
+	tmp = ddr3_cl_to_valid_cl(cl);
+	reg |= ((tmp & 0x1) << 2);
+	reg |= ((tmp & 0xE) << 3);	/* to bit 4 */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg_write(REG_DDR3_MR0_CS_ADDR +
+				  (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	/* MR1 */
+	reg = 0x00000044 & REG_DDR3_MR1_ODT_MASK;
+	if (cs_num > 1)
+		reg = 0x00000046 & REG_DDR3_MR1_ODT_MASK;
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg |= odt_static[cs_ena][cs];
+			reg_write(REG_DDR3_MR1_CS_ADDR +
+				  (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	/* MR2 */
+	if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS))
+		tmp = hclk_time / 2;
+	else
+		tmp = hclk_time;
+
+	if (tmp >= 2500)
+		cwl = 5;	/* CWL = 5 */
+	else if (tmp >= 1875 && tmp < 2500)
+		cwl = 6;	/* CWL = 6 */
+	else if (tmp >= 1500 && tmp < 1875)
+		cwl = 7;	/* CWL = 7 */
+	else if (tmp >= 1250 && tmp < 1500)
+		cwl = 8;	/* CWL = 8 */
+	else if (tmp >= 1070 && tmp < 1250)
+		cwl = 9;	/* CWL = 9 */
+	else if (tmp >= 935 && tmp < 1070)
+		cwl = 10;	/* CWL = 10 */
+	else if (tmp >= 833 && tmp < 935)
+		cwl = 11;	/* CWL = 11 */
+	else if (tmp >= 750 && tmp < 833)
+		cwl = 12;	/* CWL = 12 */
+	else {
+		cwl = 12;	/* CWL = 12 */
+		printf("Unsupported hclk %d MHz\n", tmp);
+	}
+
+	reg = ((cwl - 5) << REG_DDR3_MR2_CWL_OFFS);
+
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg &= REG_DDR3_MR2_ODT_MASK;
+			reg |= odt_dynamic[cs_ena][cs];
+			reg_write(REG_DDR3_MR2_CS_ADDR +
+				  (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	/* MR3 */
+	reg = 0x00000000;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs)) {
+			reg_write(REG_DDR3_MR3_CS_ADDR +
+				  (cs << MR_CS_ADDR_OFFS), reg);
+		}
+	}
+
+	/* {0x00001428}  -   DDR ODT Timing (Low) Register */
+	reg = 0;
+	reg |= (((cl - cwl + 1) & 0xF) << 4);
+	reg |= (((cl - cwl + 6) & 0xF) << 8);
+	reg |= ((((cl - cwl + 6) >> 4) & 0x1) << 21);
+	reg |= (((cl - 1) & 0xF) << 12);
+	reg |= (((cl + 6) & 0x1F) << 16);
+	reg_write(REG_ODT_TIME_LOW_ADDR, reg);
+
+	/* {0x0000147C}  -   DDR ODT Timing (High) Register */
+	reg = 0x00000071;
+	reg |= ((cwl - 1) << 8);
+	reg |= ((cwl + 5) << 12);
+	reg_write(REG_ODT_TIME_HIGH_ADDR, reg);
+
+#ifdef DUNIT_SPD
+	/*{0x000015E0} - DDR3 Rank Control Register */
+	reg = cs_ena;
+	cs_count = 0;
+	dimm_cnt = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (cs_ena & (1 << cs) & DIMM_CS_BITMAP) {
+			if (dimm_info[dimm_cnt].num_of_module_ranks == cs_count) {
+				dimm_cnt++;
+				cs_count = 0;
+			}
+			cs_count++;
+
+			if (dimm_info[dimm_cnt].addr_mirroring &&
+			    (cs == 1 || cs == 3) &&
+			    (sum_info.type_info != SPD_MODULE_TYPE_RDIMM)) {
+				reg |= (1 << (REG_DDR3_RANK_CTRL_MIRROR_OFFS + cs));
+				DEBUG_INIT_FULL_C("DDR3 - SPD-SET - Setting Address Mirroring for CS = ",
+						  cs, 1);
+			}
+		}
+	}
+	reg_write(REG_DDR3_RANK_CTRL_ADDR, reg);
+#endif
+
+	/*{0xD00015E4}  -   ZQDS Configuration Register */
+	reg = 0x00203c18;
+	reg_write(REG_ZQC_CONF_ADDR, reg);
+
+	/* {0x00015EC}  -   DDR PHY */
+#if defined(MV88F78X60)
+	reg = 0xF800AAA5;
+	if (mv_ctrl_rev_get() == MV_78XX0_B0_REV)
+		reg = 0xF800A225;
+#else
+	reg = 0xDE000025;
+#if defined(MV88F672X)
+	reg = 0xF800A225;
+#endif
+#endif
+	reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg);
+
+#if (defined(MV88F78X60) || defined(MV88F672X))
+	/* Registered DIMM support - supported only in AXP A0 devices */
+	/* Currently supported for SPD detection only */
+	/*
+	 * Flow is according to the Registered DIMM chapter in the
+	 * Functional Spec
+	 */
+	if (sum_info.type_info == SPD_MODULE_TYPE_RDIMM) {
+		DEBUG_INIT_S("DDR3 Training Sequence - Registered DIMM detected\n");
+
+		/* Set commands parity completion */
+		reg = reg_read(REG_REGISTERED_DRAM_CTRL_ADDR);
+		reg &= ~REG_REGISTERED_DRAM_CTRL_PARITY_MASK;
+		reg |= 0x8;
+		reg_write(REG_REGISTERED_DRAM_CTRL_ADDR, reg);
+
+		/* De-assert M_RESETn and assert M_CKE */
+		reg_write(REG_SDRAM_INIT_CTRL_ADDR,
+			  1 << REG_SDRAM_INIT_CKE_ASSERT_OFFS);
+		do {
+			reg = (reg_read(REG_SDRAM_INIT_CTRL_ADDR)) &
+				(1 << REG_SDRAM_INIT_CKE_ASSERT_OFFS);
+		} while (reg);
+
+		for (rc = 0; rc < SPD_RDIMM_RC_NUM; rc++) {
+			if (rc != 6 && rc != 7) {
+				/* Set CWA Command */
+				reg = (REG_SDRAM_OPERATION_CMD_CWA &
+				       ~(0xF << REG_SDRAM_OPERATION_CS_OFFS));
+				reg |= ((dimm_info[0].dimm_rc[rc] &
+					 REG_SDRAM_OPERATION_CWA_DATA_MASK) <<
+					REG_SDRAM_OPERATION_CWA_DATA_OFFS);
+				reg |= rc << REG_SDRAM_OPERATION_CWA_RC_OFFS;
+				/* Configure - Set Delay - tSTAB/tMRD */
+				if (rc == 2 || rc == 10)
+					reg |= (0x1 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS);
+				/* 0x1418 - SDRAM Operation Register */
+				reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+				/*
+				 * Poll the "cmd" field in the SDRAM OP
+				 * register for 0x0
+				 */
+				do {
+					reg = reg_read(REG_SDRAM_OPERATION_ADDR) &
+						(REG_SDRAM_OPERATION_CMD_MASK);
+				} while (reg);
+			}
+		}
+	}
+#endif
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_div - this function divides integers
+ * Desc:
+ * Args:     val - the value
+ *           divider - the divider
+ *           sub - substruction value
+ * Notes:
+ * Returns:  required value
+ */
+u32 ddr3_div(u32 val, u32 divider, u32 sub)
+{
+	return val / divider + (val % divider > 0 ? 1 : 0) - sub;
+}
+
+/*
+ * Name:     ddr3_get_max_val
+ * Desc:
+ * Args:
+ * Notes:
+ * Returns:
+ */
+u32 ddr3_get_max_val(u32 spd_val, u32 dimm_num, u32 static_val)
+{
+#ifdef DUNIT_STATIC
+	if (dimm_num > 0) {
+		if (spd_val >= static_val)
+			return spd_val;
+		else
+			return static_val;
+	} else {
+		return static_val;
+	}
+#else
+	return spd_val;
+#endif
+}
+
+/*
+ * Name:     ddr3_get_min_val
+ * Desc:
+ * Args:
+ * Notes:
+ * Returns:
+ */
+u32 ddr3_get_min_val(u32 spd_val, u32 dimm_num, u32 static_val)
+{
+#ifdef DUNIT_STATIC
+	if (dimm_num > 0) {
+		if (spd_val <= static_val)
+			return spd_val;
+		else
+			return static_val;
+	} else
+		return static_val;
+#else
+	return spd_val;
+#endif
+}
+#endif
diff --git a/drivers/ddr/marvell/axp/ddr3_write_leveling.c b/drivers/ddr/marvell/axp/ddr3_write_leveling.c
new file mode 100644
index 0000000..cd364ed
--- /dev/null
+++ b/drivers/ddr/marvell/axp/ddr3_write_leveling.c
@@ -0,0 +1,1365 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "ddr3_hw_training.h"
+
+/*
+ * Debug
+ */
+#define DEBUG_WL_C(s, d, l) \
+	DEBUG_WL_S(s); DEBUG_WL_D(d, l); DEBUG_WL_S("\n")
+#define DEBUG_WL_FULL_C(s, d, l) \
+	DEBUG_WL_FULL_S(s); DEBUG_WL_FULL_D(d, l); DEBUG_WL_FULL_S("\n")
+
+#ifdef MV_DEBUG_WL
+#define DEBUG_WL_S(s)			puts(s)
+#define DEBUG_WL_D(d, l)		printf("%x", d)
+#define DEBUG_RL_S(s) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%s", s)
+#define DEBUG_RL_D(d, l) \
+	debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%x", d)
+#else
+#define DEBUG_WL_S(s)
+#define DEBUG_WL_D(d, l)
+#endif
+
+#ifdef MV_DEBUG_WL_FULL
+#define DEBUG_WL_FULL_S(s)		puts(s)
+#define DEBUG_WL_FULL_D(d, l)		printf("%x", d)
+#else
+#define DEBUG_WL_FULL_S(s)
+#define DEBUG_WL_FULL_D(d, l)
+#endif
+
+#define WL_SUP_EXPECTED_DATA		0x21
+#define WL_SUP_READ_DRAM_ENTRY		0x8
+
+static int ddr3_write_leveling_single_cs(u32 cs, u32 freq, int ratio_2to1,
+					 u32 *result,
+					 MV_DRAM_INFO *dram_info);
+static void ddr3_write_ctrl_pup_reg(int bc_acc, u32 pup, u32 reg_addr,
+				    u32 data);
+
+extern u16 odt_static[ODT_OPT][MAX_CS];
+extern u16 odt_dynamic[ODT_OPT][MAX_CS];
+extern u32 wl_sup_pattern[LEN_WL_SUP_PATTERN];
+
+/*
+ * Name:     ddr3_write_leveling_hw
+ * Desc:     Execute Write leveling phase by HW
+ * Args:     freq      - current sequence frequency
+ *           dram_info   - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_write_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info)
+{
+	u32 reg, phase, delay, cs, pup;
+#ifdef MV88F67XX
+	int dpde_flag = 0;
+#endif
+	/* Debug message - Start Read leveling procedure */
+	DEBUG_WL_S("DDR3 - Write Leveling - Starting HW WL procedure\n");
+
+#ifdef MV88F67XX
+	/* Dynamic pad issue (BTS669) during WL */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) {
+		dpde_flag = 1;
+		reg_write(REG_DUNIT_CTRL_LOW_ADDR,
+			  reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS));
+	}
+#endif
+
+	reg = 1 << REG_DRAM_TRAINING_WL_OFFS;
+	/* Config the retest number */
+	reg |= (COUNT_HW_WL << REG_DRAM_TRAINING_RETEST_OFFS);
+	reg |= (dram_info->cs_ena << (REG_DRAM_TRAINING_CS_OFFS));
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	reg =  reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) |
+		(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_SHADOW_ADDR, reg);
+
+	/* Wait */
+	do {
+		reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) &
+			(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	reg = reg_read(REG_DRAM_TRAINING_ADDR);
+	/* Check if Successful */
+	if (reg & (1 << REG_DRAM_TRAINING_ERROR_OFFS)) {
+		/*
+		 * Read results to arrays - Results are required for WL
+		 * High freq Supplement and DQS Centralization
+		 */
+		for (cs = 0; cs < MAX_CS; cs++) {
+			if (dram_info->cs_ena & (1 << cs)) {
+				for (pup = 0;
+				     pup < dram_info->num_of_total_pups;
+				     pup++) {
+					if (pup == dram_info->num_of_std_pups
+					    && dram_info->ecc_ena)
+						pup = ECC_PUP;
+					reg =
+					    ddr3_read_pup_reg(PUP_WL_MODE, cs,
+							      pup);
+					phase =
+					    (reg >> REG_PHY_PHASE_OFFS) &
+					    PUP_PHASE_MASK;
+					delay = reg & PUP_DELAY_MASK;
+					dram_info->wl_val[cs][pup][P] = phase;
+					dram_info->wl_val[cs][pup][D] = delay;
+					dram_info->wl_val[cs][pup][S] =
+					    WL_HI_FREQ_STATE - 1;
+					reg =
+					    ddr3_read_pup_reg(PUP_WL_MODE + 0x1,
+							      cs, pup);
+					dram_info->wl_val[cs][pup][DQS] =
+					    (reg & 0x3F);
+				}
+
+#ifdef MV_DEBUG_WL
+				/* Debug message - Print res for cs[i]: cs,PUP,Phase,Delay */
+				DEBUG_WL_S("DDR3 - Write Leveling - Write Leveling Cs - ");
+				DEBUG_WL_D((u32) cs, 1);
+				DEBUG_WL_S(" Results:\n");
+				for (pup = 0;
+				     pup < dram_info->num_of_total_pups;
+				     pup++) {
+					if (pup == dram_info->num_of_std_pups
+					    && dram_info->ecc_ena)
+						pup = ECC_PUP;
+					DEBUG_WL_S("DDR3 - Write Leveling - PUP: ");
+					DEBUG_WL_D((u32) pup, 1);
+					DEBUG_WL_S(", Phase: ");
+					DEBUG_WL_D((u32)
+						   dram_info->wl_val[cs][pup]
+						   [P], 1);
+					DEBUG_WL_S(", Delay: ");
+					DEBUG_WL_D((u32)
+						   dram_info->wl_val[cs][pup]
+						   [D], 2);
+					DEBUG_WL_S("\n");
+				}
+#endif
+			}
+		}
+
+		/* Dynamic pad issue (BTS669) during WL */
+#ifdef MV88F67XX
+		if (dpde_flag) {
+			reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) |
+				(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS);
+			reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+		}
+#endif
+
+		DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n");
+
+		return MV_OK;
+	} else {
+		DEBUG_WL_S("DDR3 - Write Leveling - HW WL Error\n");
+		return MV_FAIL;
+	}
+}
+
+/*
+ * Name:     ddr3_wl_supplement
+ * Desc:     Write Leveling Supplement
+ * Args:     dram_info   - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_wl_supplement(MV_DRAM_INFO *dram_info)
+{
+	u32 cs, cnt, pup_num, sum, phase, delay, max_pup_num, pup, sdram_offset;
+	u32 tmp_count, ecc, reg;
+	u32 ddr_width, tmp_pup, idx;
+	u32 sdram_pup_val, uj;
+	u32 one_clk_err = 0, align_err = 0, no_err = 0, err = 0, err_n = 0;
+	u32 sdram_data[LEN_WL_SUP_PATTERN] __aligned(32) = { 0 };
+
+	ddr_width = dram_info->ddr_width;
+	no_err = 0;
+
+	DEBUG_WL_S("DDR3 - Write Leveling Hi-Freq Supplement - Starting\n");
+
+	switch (ddr_width) {
+		/* Data error from pos-adge to pos-adge */
+	case 16:
+		one_clk_err = 4;
+		align_err = 4;
+		break;
+	case 32:
+		one_clk_err = 8;
+		align_err = 8;
+		break;
+	case 64:
+		one_clk_err = 0x10;
+		align_err = 0x10;
+		break;
+	default:
+		DEBUG_WL_S("Error - bus width!!!\n");
+		return MV_FAIL;
+	}
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_WL_S("DDR3 - Write Leveling Hi-Freq Supplement - SW Override Enabled\n");
+	reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+	tmp_count = 0;
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			sum = 0;
+			/*
+			 * 2 iterations loop: 1)actual WL results 2) fix WL
+			 * if needed
+			 */
+			for (cnt = 0; cnt < COUNT_WL_HI_FREQ; cnt++) {
+				DEBUG_WL_C("COUNT = ", cnt, 1);
+				for (ecc = 0; ecc < (dram_info->ecc_ena + 1);
+				     ecc++) {
+					if (ecc) {
+						DEBUG_WL_S("ECC PUP:\n");
+					} else {
+						DEBUG_WL_S("DATA PUP:\n");
+					}
+
+					max_pup_num =
+					    dram_info->num_of_std_pups * (1 -
+									  ecc) +
+					    ecc;
+					/* ECC Support - Switch ECC Mux on ecc=1 */
+					reg =
+					    (reg_read(REG_DRAM_TRAINING_2_ADDR)
+					     & ~(1 <<
+						 REG_DRAM_TRAINING_2_ECC_MUX_OFFS));
+					reg |=
+					    (dram_info->ecc_ena *
+					     ecc <<
+					     REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
+					reg_write(REG_DRAM_TRAINING_2_ADDR,
+						  reg);
+					ddr3_reset_phy_read_fifo();
+
+					/* Write to memory */
+					sdram_offset =
+					    tmp_count * (SDRAM_CS_SIZE + 1) +
+					    0x200;
+					if (MV_OK != ddr3_dram_sram_burst((u32)
+									  wl_sup_pattern,
+									  sdram_offset,
+									  LEN_WL_SUP_PATTERN))
+						return MV_FAIL;
+
+					/* Read from memory */
+					if (MV_OK !=
+					    ddr3_dram_sram_burst(sdram_offset,
+								 (u32)
+								 sdram_data,
+								 LEN_WL_SUP_PATTERN))
+						return MV_FAIL;
+
+					/* Print the buffer */
+					for (uj = 0; uj < LEN_WL_SUP_PATTERN;
+					     uj++) {
+						if ((uj % 4 == 0) && (uj != 0)) {
+							DEBUG_WL_S("\n");
+						}
+						DEBUG_WL_D(sdram_data[uj],
+							   8);
+						DEBUG_WL_S(" ");
+					}
+
+					/* Check pup which DQS/DATA is error */
+					for (pup = 0; pup < max_pup_num; pup++) {
+						/* ECC support - bit 8 */
+						pup_num = (ecc) ? ECC_PUP : pup;
+						if (pup < 4) {	/* lower 32 bit */
+							tmp_pup = pup;
+							idx =
+							    WL_SUP_READ_DRAM_ENTRY;
+						} else {	/* higher 32 bit */
+							tmp_pup = pup - 4;
+							idx =
+							    WL_SUP_READ_DRAM_ENTRY
+							    + 1;
+						}
+						DEBUG_WL_S("\nCS: ");
+						DEBUG_WL_D((u32) cs, 1);
+						DEBUG_WL_S(" PUP: ");
+						DEBUG_WL_D((u32) pup_num, 1);
+						DEBUG_WL_S("\n");
+						sdram_pup_val =
+						    ((sdram_data[idx] >>
+						      ((tmp_pup) * 8)) & 0xFF);
+						DEBUG_WL_C("Actual Data = ",
+							   sdram_pup_val, 2);
+						DEBUG_WL_C("Expected Data = ",
+							   (WL_SUP_EXPECTED_DATA
+							    + pup), 2);
+						/*
+						 * ALINGHMENT: calculate
+						 * expected data vs actual data
+						 */
+						err =
+						    (WL_SUP_EXPECTED_DATA +
+						     pup) - sdram_pup_val;
+						/*
+						 * CLOCK LONG: calculate
+						 * expected data vs actual data
+						 */
+						err_n =
+						    sdram_pup_val -
+						    (WL_SUP_EXPECTED_DATA +
+						     pup);
+						DEBUG_WL_C("err = ", err, 2);
+						DEBUG_WL_C("err_n = ", err_n,
+							   2);
+						if (err == no_err) {
+							/* PUP is correct - increment State */
+							dram_info->wl_val[cs]
+							    [pup_num]
+							    [S] = 1;
+						} else if (err_n == one_clk_err) {
+							/* clock is longer than DQS */
+							phase =
+							    ((dram_info->wl_val
+							      [cs]
+							      [pup_num][P] +
+							      WL_HI_FREQ_SHIFT)
+							     % MAX_PHASE_2TO1);
+							dram_info->wl_val[cs]
+							    [pup_num]
+							    [P] = phase;
+							delay =
+							    dram_info->wl_val
+							    [cs][pup_num]
+							    [D];
+							DEBUG_WL_S("#### Clock is longer than DQS more than one clk cycle ####\n");
+							ddr3_write_pup_reg
+							    (PUP_WL_MODE, cs,
+							     pup * (1 - ecc) +
+							     ECC_PUP * ecc,
+							     phase, delay);
+						} else if (err == align_err) {
+							/* clock is align to DQS */
+							phase =
+							    dram_info->wl_val
+							    [cs][pup_num]
+							    [P];
+							delay =
+							    dram_info->wl_val
+							    [cs][pup_num]
+							    [D];
+							DEBUG_WL_S("#### Alignment PUPS problem ####\n");
+							if ((phase == 0)
+							    || ((phase == 1)
+								&& (delay <=
+								    0x10))) {
+								DEBUG_WL_S("#### Warning - Possible Layout Violation (DQS is longer than CLK)####\n");
+							}
+
+							phase = 0x0;
+							delay = 0x0;
+							dram_info->wl_val[cs]
+							    [pup_num]
+							    [P] = phase;
+							dram_info->wl_val[cs]
+							    [pup_num]
+							    [D] = delay;
+							ddr3_write_pup_reg
+							    (PUP_WL_MODE, cs,
+							     pup * (1 - ecc) +
+							     ECC_PUP * ecc,
+							     phase, delay);
+						}
+						/* Stop condition for ECC phase */
+						pup = (ecc) ? max_pup_num : pup;
+					}
+
+					/* ECC Support - Disable ECC MUX */
+					reg =
+					    (reg_read(REG_DRAM_TRAINING_2_ADDR)
+					     & ~(1 <<
+						 REG_DRAM_TRAINING_2_ECC_MUX_OFFS));
+					reg_write(REG_DRAM_TRAINING_2_ADDR,
+						  reg);
+				}
+			}
+
+			for (pup = 0; pup < dram_info->num_of_std_pups; pup++)
+				sum += dram_info->wl_val[cs][pup][S];
+
+			if (dram_info->ecc_ena)
+				sum += dram_info->wl_val[cs][ECC_PUP][S];
+
+			/* Checks if any pup is not locked after the change */
+			if (sum < (WL_HI_FREQ_STATE * (dram_info->num_of_total_pups))) {
+				DEBUG_WL_C("DDR3 - Write Leveling Hi-Freq Supplement - didn't work for Cs - ",
+					   (u32) cs, 1);
+				return MV_FAIL;
+			}
+			tmp_count++;
+		}
+	}
+
+	dram_info->wl_max_phase = 0;
+	dram_info->wl_min_phase = 10;
+
+	/*
+	 * Read results to arrays - Results are required for DQS Centralization
+	 */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			for (pup = 0; pup < dram_info->num_of_total_pups; pup++) {
+				if (pup == dram_info->num_of_std_pups
+				    && dram_info->ecc_ena)
+					pup = ECC_PUP;
+				reg = ddr3_read_pup_reg(PUP_WL_MODE, cs, pup);
+				phase =
+				    (reg >> REG_PHY_PHASE_OFFS) &
+				    PUP_PHASE_MASK;
+				if (phase > dram_info->wl_max_phase)
+					dram_info->wl_max_phase = phase;
+				if (phase < dram_info->wl_min_phase)
+					dram_info->wl_min_phase = phase;
+			}
+		}
+	}
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
+		(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
+	reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
+
+	DEBUG_WL_S("DDR3 - Write Leveling Hi-Freq Supplement - Ended Successfully\n");
+
+	return MV_OK;
+}
+
+/*
+ * Name:     ddr3_write_leveling_hw_reg_dimm
+ * Desc:     Execute Write leveling phase by HW
+ * Args:     freq      - current sequence frequency
+ *           dram_info   - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_write_leveling_hw_reg_dimm(u32 freq, MV_DRAM_INFO *dram_info)
+{
+	u32 reg, phase, delay, cs, pup, pup_num;
+	__maybe_unused int dpde_flag = 0;
+
+	/* Debug message - Start Read leveling procedure */
+	DEBUG_WL_S("DDR3 - Write Leveling - Starting HW WL procedure\n");
+
+	if (dram_info->num_cs > 2) {
+		DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n");
+		return MV_NO_CHANGE;
+	}
+
+	/* If target freq = 400 move clock start point */
+	/* Write to control PUP to Control Deskew Regs */
+	if (freq <= DDR_400) {
+		for (pup = 0; pup <= dram_info->num_of_total_pups; pup++) {
+			/* PUP_DELAY_MASK 0x1F */
+			/* reg = 0x0C10001F + (uj << 16); */
+			ddr3_write_ctrl_pup_reg(1, pup, CNTRL_PUP_DESKEW + pup,
+						0x1F);
+		}
+	}
+
+#ifdef MV88F67XX
+	/* Dynamic pad issue (BTS669) during WL */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) {
+		dpde_flag = 1;
+		reg_write(REG_DUNIT_CTRL_LOW_ADDR,
+			  reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS));
+	}
+#endif
+
+	reg = (1 << REG_DRAM_TRAINING_WL_OFFS);
+	/* Config the retest number */
+	reg |= (COUNT_HW_WL << REG_DRAM_TRAINING_RETEST_OFFS);
+	reg |= (dram_info->cs_ena << (REG_DRAM_TRAINING_CS_OFFS));
+	reg_write(REG_DRAM_TRAINING_ADDR, reg);	/* 0x15B0 - Training Register */
+
+	reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) |
+		(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	reg_write(REG_DRAM_TRAINING_SHADOW_ADDR, reg);
+
+	/* Wait */
+	do {
+		reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) &
+			(1 << REG_DRAM_TRAINING_AUTO_OFFS);
+	} while (reg);		/* Wait for '0' */
+
+	reg = reg_read(REG_DRAM_TRAINING_ADDR);
+	/* Check if Successful */
+	if (reg & (1 << REG_DRAM_TRAINING_ERROR_OFFS)) {
+		/*
+		 * Read results to arrays - Results are required for WL High
+		 * freq Supplement and DQS Centralization
+		 */
+		for (cs = 0; cs < MAX_CS; cs++) {
+			if (dram_info->cs_ena & (1 << cs)) {
+				for (pup = 0;
+				     pup < dram_info->num_of_total_pups;
+				     pup++) {
+					if (pup == dram_info->num_of_std_pups
+					    && dram_info->ecc_ena)
+						pup = ECC_BIT;
+					reg =
+					    ddr3_read_pup_reg(PUP_WL_MODE, cs,
+							      pup);
+					phase =
+					    (reg >> REG_PHY_PHASE_OFFS) &
+					    PUP_PHASE_MASK;
+					delay = reg & PUP_DELAY_MASK;
+					dram_info->wl_val[cs][pup][P] = phase;
+					dram_info->wl_val[cs][pup][D] = delay;
+					if ((phase == 1) && (delay >= 0x1D)) {
+						/*
+						 * Need to do it here for
+						 * uncorrect WL values
+						 */
+						ddr3_write_pup_reg(PUP_WL_MODE,
+								   cs, pup, 0,
+								   0);
+						dram_info->wl_val[cs][pup][P] =
+						    0;
+						dram_info->wl_val[cs][pup][D] =
+						    0;
+					}
+					dram_info->wl_val[cs][pup][S] =
+					    WL_HI_FREQ_STATE - 1;
+					reg =
+					    ddr3_read_pup_reg(PUP_WL_MODE + 0x1,
+							      cs, pup);
+					dram_info->wl_val[cs][pup][DQS] =
+					    (reg & 0x3F);
+				}
+#ifdef MV_DEBUG_WL
+				/*
+				 * Debug message - Print res for cs[i]:
+				 * cs,PUP,Phase,Delay
+				 */
+				DEBUG_WL_S("DDR3 - Write Leveling - Write Leveling Cs - ");
+				DEBUG_WL_D((u32) cs, 1);
+				DEBUG_WL_S(" Results:\n");
+				for (pup = 0;
+				     pup < dram_info->num_of_total_pups;
+				     pup++) {
+					DEBUG_WL_S
+					    ("DDR3 - Write Leveling - PUP: ");
+					DEBUG_WL_D((u32) pup, 1);
+					DEBUG_WL_S(", Phase: ");
+					DEBUG_WL_D((u32)
+						   dram_info->wl_val[cs][pup]
+						   [P], 1);
+					DEBUG_WL_S(", Delay: ");
+					DEBUG_WL_D((u32)
+						   dram_info->wl_val[cs][pup]
+						   [D], 2);
+					DEBUG_WL_S("\n");
+				}
+#endif
+			}
+		}
+
+#ifdef MV88F67XX
+		/* Dynamic pad issue (BTS669) during WL */
+		if (dpde_flag) {
+			reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) |
+				(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS);
+			reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+		}
+#endif
+		DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n");
+
+		/* If target freq = 400 move clock back */
+		/* Write to control PUP to Control Deskew Regs */
+		if (freq <= DDR_400) {
+			for (pup = 0; pup <= dram_info->num_of_total_pups;
+			     pup++) {
+				ddr3_write_ctrl_pup_reg(1, pup,
+							CNTRL_PUP_DESKEW + pup, 0);
+			}
+		}
+
+		return MV_OK;
+	} else {
+		/* Configure Each PUP with locked leveling settings */
+		for (cs = 0; cs < MAX_CS; cs++) {
+			if (dram_info->cs_ena & (1 << cs)) {
+				for (pup = 0;
+				     pup < dram_info->num_of_total_pups;
+				     pup++) {
+					/* ECC support - bit 8 */
+					pup_num = (pup == dram_info->num_of_std_pups) ?
+						ECC_BIT : pup;
+					ddr3_write_pup_reg(PUP_WL_MODE, cs,
+							   pup_num, 0, 0);
+				}
+			}
+		}
+
+		reg_write(REG_DRAM_TRAINING_ADDR, 0);
+
+		/* If target freq = 400 move clock back */
+		/* Write to control PUP to Control Deskew Regs */
+		if (freq <= DDR_400) {
+			for (pup = 0; pup <= dram_info->num_of_total_pups;
+			     pup++) {
+				ddr3_write_ctrl_pup_reg(1, pup,
+							CNTRL_PUP_DESKEW + pup, 0);
+			}
+		}
+
+		DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n");
+		return MV_NO_CHANGE;
+	}
+}
+
+/*
+ * Name:     ddr3_write_leveling_sw
+ * Desc:     Execute Write leveling phase by SW
+ * Args:     freq      - current sequence frequency
+ *           dram_info   - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_write_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info)
+{
+	u32 reg, cs, cnt, pup, max_pup_num;
+	u32 res[MAX_CS];
+	max_pup_num = dram_info->num_of_total_pups;
+	__maybe_unused int dpde_flag = 0;
+
+	/* Debug message - Start Write leveling procedure */
+	DEBUG_WL_S("DDR3 - Write Leveling - Starting SW WL procedure\n");
+
+#ifdef MV88F67XX
+	/* Dynamic pad issue (BTS669) during WL */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) {
+		dpde_flag = 1;
+		reg_write(REG_DUNIT_CTRL_LOW_ADDR,
+			  reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS));
+	}
+#endif
+
+	/* Set Output buffer-off to all CS and correct ODT values */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_ODT_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+			reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS);
+
+			/* 0x15D0 - DDR3 MR0 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+		}
+	}
+
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling - Qoff and RTT Values are set for all Cs\n");
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling - SW Override Enabled\n");
+
+	/* Enable PHY write leveling mode */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+		~(1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS);
+	/* [2] = 0 - TrnWLMode - Enable */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	/* Reset WL results arry */
+	memset(dram_info->wl_val, 0, sizeof(u32) * MAX_CS * MAX_PUP_NUM * 7);
+
+	/* Loop for each cs */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling - Starting working with Cs - ",
+					(u32) cs, 1);
+			/* Refresh X9 current cs */
+			DEBUG_WL_FULL_S("DDR3 - Write Leveling - Refresh X9\n");
+			for (cnt = 0; cnt < COUNT_WL_RFRS; cnt++) {
+				reg =
+				    REG_SDRAM_OPERATION_CMD_RFRS & ~(1 <<
+								     (REG_SDRAM_OPERATION_CS_OFFS
+								      + cs));
+				/* [3-0] = 0x2 - refresh, [11-8] - enable current cs */
+				reg_write(REG_SDRAM_OPERATION_ADDR, reg);	/* 0x1418 - SDRAM Operation Register */
+
+				do {
+					reg =
+					    ((reg_read
+					      (REG_SDRAM_OPERATION_ADDR)) &
+					     REG_SDRAM_OPERATION_CMD_RFRS_DONE);
+				} while (reg);	/* Wait for '0' */
+			}
+
+			/* Configure MR1 in Cs[CsNum] - write leveling on, output buffer on */
+			DEBUG_WL_FULL_S("DDR3 - Write Leveling - Configure MR1 for current Cs: WL-on,OB-on\n");
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_OUTBUF_WL_MASK;
+			/* Set ODT Values */
+			reg &= REG_DDR3_MR1_ODT_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+			/* Enable WL MODE */
+			reg |= (1 << REG_DDR3_MR1_WL_ENA_OFFS);
+			/* [7]=1, [12]=0 - Output Buffer and write leveling enabled */
+			reg_write(REG_DDR3_MR1_ADDR, reg);	/* 0x15D4 - DDR3 MR1 Register */
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+
+			/* Write leveling  cs[cs] */
+			if (MV_OK !=
+			    ddr3_write_leveling_single_cs(cs, freq, ratio_2to1,
+							  (u32 *)(res + cs),
+							  dram_info)) {
+				DEBUG_WL_FULL_C("DDR3 - Write Leveling single Cs - FAILED -  Cs - ",
+						(u32) cs, 1);
+				for (pup = 0; pup < max_pup_num; pup++) {
+					if (((res[cs] >> pup) & 0x1) == 0) {
+						DEBUG_WL_C("Failed Byte : ",
+							   pup, 1);
+					}
+				}
+				return MV_FAIL;
+			}
+
+			/* Set TrnWLDeUpd - After each CS is done */
+			reg = reg_read(REG_TRAINING_WL_ADDR) |
+				(1 << REG_TRAINING_WL_CS_DONE_OFFS);
+			/* 0x16AC - Training Write leveling register */
+			reg_write(REG_TRAINING_WL_ADDR, reg);
+
+			/*
+			 * Debug message - Finished Write leveling cs[cs] -
+			 * each PUP Fail/Success
+			 */
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling - Finished Cs - ", (u32) cs,
+					1);
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling - The Results: 1-PUP locked, 0-PUP failed -",
+					(u32) res[cs], 3);
+
+			/*
+			 * Configure MR1 in cs[cs] - write leveling off (0),
+			 * output buffer off (1)
+			 */
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_OUTBUF_WL_MASK;
+			reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS);
+			/* No need to sort ODT since it is same CS */
+			/* 0x15D4 - DDR3 MR1 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+		}
+	}
+
+	/* Disable WL Mode */
+	/* [2]=1 - TrnWLMode - Disable */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg |= (1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Set Output buffer-on to all CS and correct ODT values */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_ODT_MASK;
+			reg &= REG_DDR3_MR1_OUTBUF_WL_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+
+			/* 0x15D0 - DDR3 MR1 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+		}
+	}
+
+#ifdef MV88F67XX
+	/* Dynamic pad issue (BTS669) during WL */
+	if (dpde_flag) {
+		reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) |
+			(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS);
+		reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+	}
+#endif
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling - Finished WL procedure for all Cs\n");
+
+	return MV_OK;
+}
+
+#if !defined(MV88F672X)
+/*
+ * Name:     ddr3_write_leveling_sw
+ * Desc:     Execute Write leveling phase by SW
+ * Args:     freq        - current sequence frequency
+ *           dram_info   - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+int ddr3_write_leveling_sw_reg_dimm(u32 freq, int ratio_2to1,
+				    MV_DRAM_INFO *dram_info)
+{
+	u32 reg, cs, cnt, pup;
+	u32 res[MAX_CS];
+	__maybe_unused int dpde_flag = 0;
+
+	/* Debug message - Start Write leveling procedure */
+	DEBUG_WL_S("DDR3 - Write Leveling - Starting SW WL procedure\n");
+
+#ifdef MV88F67XX
+	/* Dynamic pad issue (BTS669) during WL */
+	reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR);
+	if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) {
+		dpde_flag = 1;
+		reg_write(REG_DUNIT_CTRL_LOW_ADDR,
+			  reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS));
+	}
+#endif
+
+	/* If target freq = 400 move clock start point */
+	/* Write to control PUP to Control Deskew Regs */
+	if (freq <= DDR_400) {
+		for (pup = 0; pup <= dram_info->num_of_total_pups; pup++) {
+			/* PUP_DELAY_MASK 0x1F */
+			/* reg = 0x0C10001F + (uj << 16); */
+			ddr3_write_ctrl_pup_reg(1, pup, CNTRL_PUP_DESKEW + pup,
+						0x1F);
+		}
+	}
+
+	/* Set Output buffer-off to all CS and correct ODT values */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_ODT_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+			reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS);
+
+			/* 0x15D0 - DDR3 MR0 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+		}
+	}
+
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling - Qoff and RTT Values are set for all Cs\n");
+
+	/* Enable SW override */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
+		(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* [0] = 1 - Enable SW override  */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling - SW Override Enabled\n");
+
+	/* Enable PHY write leveling mode */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
+		~(1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS);
+	/* [2] = 0 - TrnWLMode - Enable */
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Loop for each cs */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling - Starting working with Cs - ",
+					(u32) cs, 1);
+
+			/* Refresh X9 current cs */
+			DEBUG_WL_FULL_S("DDR3 - Write Leveling - Refresh X9\n");
+			for (cnt = 0; cnt < COUNT_WL_RFRS; cnt++) {
+				reg =
+				    REG_SDRAM_OPERATION_CMD_RFRS & ~(1 <<
+								     (REG_SDRAM_OPERATION_CS_OFFS
+								      + cs));
+				/* [3-0] = 0x2 - refresh, [11-8] - enable current cs */
+				reg_write(REG_SDRAM_OPERATION_ADDR, reg);	/* 0x1418 - SDRAM Operation Register */
+
+				do {
+					reg =
+					    ((reg_read
+					      (REG_SDRAM_OPERATION_ADDR)) &
+					     REG_SDRAM_OPERATION_CMD_RFRS_DONE);
+				} while (reg);	/* Wait for '0' */
+			}
+
+			/*
+			 * Configure MR1 in Cs[CsNum] - write leveling on,
+			 * output buffer on
+			 */
+			DEBUG_WL_FULL_S("DDR3 - Write Leveling - Configure MR1 for current Cs: WL-on,OB-on\n");
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_OUTBUF_WL_MASK;
+			/* Set ODT Values */
+			reg &= REG_DDR3_MR1_ODT_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+			/* Enable WL MODE */
+			reg |= (1 << REG_DDR3_MR1_WL_ENA_OFFS);
+			/*
+			 * [7]=1, [12]=0 - Output Buffer and write leveling
+			 * enabled
+			 */
+			/* 0x15D4 - DDR3 MR1 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+
+			/* Write leveling  cs[cs] */
+			if (MV_OK !=
+			    ddr3_write_leveling_single_cs(cs, freq, ratio_2to1,
+							  (u32 *)(res + cs),
+							  dram_info)) {
+				DEBUG_WL_FULL_C("DDR3 - Write Leveling single Cs - FAILED -  Cs - ",
+						(u32) cs, 1);
+				return MV_FAIL;
+			}
+
+			/* Set TrnWLDeUpd - After each CS is done */
+			reg = reg_read(REG_TRAINING_WL_ADDR) |
+				(1 << REG_TRAINING_WL_CS_DONE_OFFS);
+			/* 0x16AC - Training Write leveling register */
+			reg_write(REG_TRAINING_WL_ADDR, reg);
+
+			/*
+			 * Debug message - Finished Write leveling cs[cs] -
+			 * each PUP Fail/Success
+			 */
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling - Finished Cs - ", (u32) cs,
+					1);
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling - The Results: 1-PUP locked, 0-PUP failed -",
+					(u32) res[cs], 3);
+
+			/* Configure MR1 in cs[cs] - write leveling off (0), output buffer off (1) */
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_OUTBUF_WL_MASK;
+			reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS);
+			/* No need to sort ODT since it is same CS */
+			/* 0x15D4 - DDR3 MR1 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+		}
+	}
+
+	/* Disable WL Mode */
+	/* [2]=1 - TrnWLMode - Disable */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg |= (1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Disable SW override - Must be in a different stage */
+	/* [0]=0 - Enable SW override  */
+	reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
+	reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
+	/* 0x15B8 - Training SW 2 Register */
+	reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
+
+	/* Set Output buffer-on to all CS and correct ODT values */
+	for (cs = 0; cs < MAX_CS; cs++) {
+		if (dram_info->cs_ena & (1 << cs)) {
+			reg = reg_read(REG_DDR3_MR1_ADDR) &
+				REG_DDR3_MR1_ODT_MASK;
+			reg &= REG_DDR3_MR1_OUTBUF_WL_MASK;
+			reg |= odt_static[dram_info->cs_ena][cs];
+
+			/* 0x15D0 - DDR3 MR1 Register */
+			reg_write(REG_DDR3_MR1_ADDR, reg);
+			/* Issue MRS Command to current cs */
+			reg = REG_SDRAM_OPERATION_CMD_MR1 &
+				~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs));
+			/*
+			 * [3-0] = 0x4 - MR1 Command, [11-8] -
+			 * enable current cs
+			 */
+			/* 0x1418 - SDRAM Operation Register */
+			reg_write(REG_SDRAM_OPERATION_ADDR, reg);
+
+			udelay(MRS_DELAY);
+		}
+	}
+
+#ifdef MV88F67XX
+	/* Dynamic pad issue (BTS669) during WL */
+	if (dpde_flag) {
+		reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) |
+			(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS);
+		reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg);
+	}
+#endif
+
+	/* If target freq = 400 move clock back */
+	/* Write to control PUP to Control Deskew Regs */
+	if (freq <= DDR_400) {
+		for (pup = 0; pup <= dram_info->num_of_total_pups; pup++) {
+			ddr3_write_ctrl_pup_reg(1, pup, CNTRL_PUP_DESKEW + pup,
+						0);
+		}
+	}
+
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling - Finished WL procedure for all Cs\n");
+	return MV_OK;
+}
+#endif
+
+/*
+ * Name:     ddr3_write_leveling_single_cs
+ * Desc:     Execute Write leveling for single Chip select
+ * Args:     cs          - current chip select
+ *           freq        - current sequence frequency
+ *           result      - res array
+ *           dram_info   - main struct
+ * Notes:
+ * Returns:  MV_OK if success, MV_FAIL if fail.
+ */
+static int ddr3_write_leveling_single_cs(u32 cs, u32 freq, int ratio_2to1,
+					 u32 *result, MV_DRAM_INFO *dram_info)
+{
+	u32 reg, pup_num, delay, phase, phaseMax, max_pup_num, pup,
+		max_pup_mask;
+
+	max_pup_num = dram_info->num_of_total_pups;
+	*result = 0;
+	u32 flag[MAX_PUP_NUM] = { 0 };
+
+	DEBUG_WL_FULL_C("DDR3 - Write Leveling Single Cs - WL for Cs - ",
+			(u32) cs, 1);
+
+	switch (max_pup_num) {
+	case 2:
+		max_pup_mask = 0x3;
+		break;
+	case 4:
+		max_pup_mask = 0xf;
+		DEBUG_WL_C("max_pup_mask =  ", max_pup_mask, 3);
+		break;
+	case 5:
+		max_pup_mask = 0x1f;
+		DEBUG_WL_C("max_pup_mask =  ", max_pup_mask, 3);
+		break;
+	case 8:
+		max_pup_mask = 0xff;
+		DEBUG_WL_C("max_pup_mask =  ", max_pup_mask, 3);
+		break;
+	case 9:
+		max_pup_mask = 0x1ff;
+		DEBUG_WL_C("max_pup_mask =  ", max_pup_mask, 3);
+		break;
+	default:
+		DEBUG_WL_C("ddr3_write_leveling_single_cs wrong max_pup_num =  ",
+			   max_pup_num, 3);
+		return MV_FAIL;
+	}
+
+	/* CS ODT Override */
+	reg = reg_read(REG_SDRAM_ODT_CTRL_HIGH_ADDR) &
+		REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK;
+	reg |= (REG_SDRAM_ODT_CTRL_HIGH_OVRD_ENA << (2 * cs));
+	/* Set 0x3 - Enable ODT on the curent cs and disable on other cs */
+	/* 0x1498 - SDRAM ODT Control high */
+	reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, reg);
+
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - ODT Asserted for current Cs\n");
+
+	/* tWLMRD Delay */
+	/* Delay of minimum 40 Dram clock cycles - 20 Tclk cycles */
+	udelay(1);
+
+	/* [1:0] - current cs number */
+	reg = (reg_read(REG_TRAINING_WL_ADDR) & REG_TRAINING_WL_CS_MASK) | cs;
+	reg |= (1 << REG_TRAINING_WL_UPD_OFFS);	/* [2] - trnWLCsUpd */
+	/* 0x16AC - Training Write leveling register */
+	reg_write(REG_TRAINING_WL_ADDR, reg);
+
+	/* Broadcast to all PUPs: Reset DQS phase, reset leveling delay */
+	ddr3_write_pup_reg(PUP_WL_MODE, cs, PUP_BC, 0, 0);
+
+	/* Seek Edge */
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge - Current Cs\n");
+
+	/* Drive DQS high for one cycle - All data PUPs */
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge - Driving DQS high for one cycle\n");
+	if (!ratio_2to1) {
+		reg = (reg_read(REG_TRAINING_WL_ADDR) &
+		       REG_TRAINING_WL_RATIO_MASK) | REG_TRAINING_WL_1TO1;
+	} else {
+		reg = (reg_read(REG_TRAINING_WL_ADDR) &
+		       REG_TRAINING_WL_RATIO_MASK) | REG_TRAINING_WL_2TO1;
+	}
+	/* 0x16AC - Training Write leveling register */
+	reg_write(REG_TRAINING_WL_ADDR, reg);
+
+	/* Wait tWLdelay */
+	do {
+		/* [29] - trnWLDelayExp */
+		reg = (reg_read(REG_TRAINING_WL_ADDR)) &
+			REG_TRAINING_WL_DELAYEXP_MASK;
+	} while (reg == 0x0);	/* Wait for '1' */
+
+	/* Read WL res */
+	reg = (reg_read(REG_TRAINING_WL_ADDR) >> REG_TRAINING_WL_RESULTS_OFFS) &
+		REG_TRAINING_WL_RESULTS_MASK;
+	/* [28:20] - TrnWLResult */
+
+	if (!ratio_2to1) /* Different phase options for 2:1 or 1:1 modes */
+		phaseMax = MAX_PHASE_1TO1;
+	else
+		phaseMax = MAX_PHASE_2TO1;
+
+	DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge - Shift DQS + Octet Leveling\n");
+
+	/* Shift DQS + Octet leveling */
+	for (phase = 0; phase < phaseMax; phase++) {
+		for (delay = 0; delay < MAX_DELAY; delay++) {
+			/*  Broadcast to all PUPs: DQS phase,leveling delay */
+			ddr3_write_pup_reg(PUP_WL_MODE, cs, PUP_BC, phase,
+					   delay);
+
+			udelay(1);	/* Delay of  3 Tclk cycles */
+
+			DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge: Phase = ");
+			DEBUG_WL_FULL_D((u32) phase, 1);
+			DEBUG_WL_FULL_S(", Delay = ");
+			DEBUG_WL_FULL_D((u32) delay, 1);
+			DEBUG_WL_FULL_S("\n");
+
+			/* Drive DQS high for one cycle - All data PUPs */
+			if (!ratio_2to1) {
+				reg = (reg_read(REG_TRAINING_WL_ADDR) &
+				       REG_TRAINING_WL_RATIO_MASK) |
+					REG_TRAINING_WL_1TO1;
+			} else {
+				reg = (reg_read(REG_TRAINING_WL_ADDR) &
+				       REG_TRAINING_WL_RATIO_MASK) |
+					REG_TRAINING_WL_2TO1;
+			}
+			reg_write(REG_TRAINING_WL_ADDR, reg);	/* 0x16AC  */
+
+			/* Wait tWLdelay */
+			do {
+				reg = (reg_read(REG_TRAINING_WL_ADDR)) &
+					REG_TRAINING_WL_DELAYEXP_MASK;
+			} while (reg == 0x0);	/* [29] Wait for '1' */
+
+			/* Read WL res */
+			reg = reg_read(REG_TRAINING_WL_ADDR);
+			reg = (reg >> REG_TRAINING_WL_RESULTS_OFFS) &
+				REG_TRAINING_WL_RESULTS_MASK;	/* [28:20] */
+
+			DEBUG_WL_FULL_C("DDR3 - Write Leveling Single Cs - Seek Edge: Results =  ",
+					(u32) reg, 3);
+
+			/* Update State machine */
+			for (pup = 0; pup < (max_pup_num); pup++) {
+				/* ECC support - bit 8 */
+				pup_num = (pup == dram_info->num_of_std_pups) ?
+					ECC_BIT : pup;
+				if (dram_info->wl_val[cs][pup][S] == 0) {
+					/* Update phase to PUP */
+					dram_info->wl_val[cs][pup][P] = phase;
+					/* Update delay to PUP */
+					dram_info->wl_val[cs][pup][D] = delay;
+				}
+
+				if (((reg >> pup_num) & 0x1) == 0)
+					flag[pup_num] = 1;
+
+				if (((reg >> pup_num) & 0x1)
+				    && (flag[pup_num] == 1)
+				    && (dram_info->wl_val[cs][pup][S] == 0)) {
+					/*
+					 * If the PUP is locked now and in last
+					 * counter states
+					 */
+					/* Go to next state */
+					dram_info->wl_val[cs][pup][S] = 1;
+					/* Set res */
+					*result = *result | (1 << pup_num);
+				}
+			}
+
+			/* If all locked - Break the loops - Finished */
+			if (*result == max_pup_mask) {
+				phase = phaseMax;
+				delay = MAX_DELAY;
+				DEBUG_WL_S("DDR3 - Write Leveling Single Cs - Seek Edge: All Locked\n");
+			}
+		}
+	}
+
+	/* Debug message - Print res for cs[i]: cs,PUP,Phase,Delay */
+	DEBUG_WL_C("DDR3 - Write Leveling - Results for CS - ", (u32) cs, 1);
+	for (pup = 0; pup < (max_pup_num); pup++) {
+		DEBUG_WL_S("DDR3 - Write Leveling - PUP: ");
+		DEBUG_WL_D((u32) pup, 1);
+		DEBUG_WL_S(", Phase: ");
+		DEBUG_WL_D((u32) dram_info->wl_val[cs][pup][P], 1);
+		DEBUG_WL_S(", Delay: ");
+		DEBUG_WL_D((u32) dram_info->wl_val[cs][pup][D], 2);
+		DEBUG_WL_S("\n");
+	}
+
+	/* Check if some not locked and return error */
+	if (*result != max_pup_mask) {
+		DEBUG_WL_S("DDR3 - Write Leveling - ERROR - not all PUPS were locked\n");
+		return MV_FAIL;
+	}
+
+	/* Configure Each PUP with locked leveling settings */
+	for (pup = 0; pup < (max_pup_num); pup++) {
+		/* ECC support - bit 8 */
+		pup_num = (pup == dram_info->num_of_std_pups) ? ECC_BIT : pup;
+		phase = dram_info->wl_val[cs][pup][P];
+		delay = dram_info->wl_val[cs][pup][D];
+		ddr3_write_pup_reg(PUP_WL_MODE, cs, pup_num, phase, delay);
+	}
+
+	/* CS ODT Override */
+	reg =  reg_read(REG_SDRAM_ODT_CTRL_HIGH_ADDR) &
+		REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK;
+	/* 0x1498 - SDRAM ODT Control high */
+	reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, reg);
+
+	return MV_OK;
+}
+
+/*
+ * Perform DDR3 Control PUP Indirect Write
+ */
+static void ddr3_write_ctrl_pup_reg(int bc_acc, u32 pup, u32 reg_addr, u32 data)
+{
+	u32 reg = 0;
+
+	/* Store value for write */
+	reg = (data & 0xFFFF);
+
+	/* Set bit 26 for control PHY access */
+	reg |= (1 << REG_PHY_CNTRL_OFFS);
+
+	/* Configure BC or UC access to PHYs */
+	if (bc_acc == 1)
+		reg |= (1 << REG_PHY_BC_OFFS);
+	else
+		reg |= (pup << REG_PHY_PUP_OFFS);
+
+	/* Set PHY register address to write to */
+	reg |= (reg_addr << REG_PHY_CS_OFFS);
+
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+	reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
+	reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg);	/* 0x16A0 */
+
+	do {
+		reg = (reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR)) &
+			REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
+	} while (reg);		/* Wait for '0' to mark the end of the transaction */
+}
diff --git a/drivers/ddr/marvell/axp/xor.c b/drivers/ddr/marvell/axp/xor.c
new file mode 100644
index 0000000..31e6582
--- /dev/null
+++ b/drivers/ddr/marvell/axp/xor.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+#include "xor.h"
+#include "xor_regs.h"
+
+static u32 xor_regs_ctrl_backup;
+static u32 xor_regs_base_backup[MAX_CS];
+static u32 xor_regs_mask_backup[MAX_CS];
+
+static int mv_xor_cmd_set(u32 chan, int command);
+static int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl);
+
+void mv_sys_xor_init(MV_DRAM_INFO *dram_info)
+{
+	u32 reg, ui, base, cs_count;
+
+	xor_regs_ctrl_backup = reg_read(XOR_WINDOW_CTRL_REG(0, 0));
+	for (ui = 0; ui < MAX_CS; ui++)
+		xor_regs_base_backup[ui] = reg_read(XOR_BASE_ADDR_REG(0, ui));
+	for (ui = 0; ui < MAX_CS; ui++)
+		xor_regs_mask_backup[ui] = reg_read(XOR_SIZE_MASK_REG(0, ui));
+
+	reg = 0;
+	for (ui = 0; ui < (dram_info->num_cs + 1); ui++) {
+		/* Enable Window x for each CS */
+		reg |= (0x1 << (ui));
+		/* Enable Window x for each CS */
+		reg |= (0x3 << ((ui * 2) + 16));
+	}
+
+	reg_write(XOR_WINDOW_CTRL_REG(0, 0), reg);
+
+	/* Last window - Base - 0x40000000, Attribute 0x1E - SRAM */
+	base = (SRAM_BASE & 0xFFFF0000) | 0x1E00;
+	reg_write(XOR_BASE_ADDR_REG(0, dram_info->num_cs), base);
+	/* Last window - Size - 64 MB */
+	reg_write(XOR_SIZE_MASK_REG(0, dram_info->num_cs), 0x03FF0000);
+
+	cs_count = 0;
+	for (ui = 0; ui < MAX_CS; ui++) {
+		if (dram_info->cs_ena & (1 << ui)) {
+			/*
+			 * Window x - Base - 0x00000000, Attribute 0x0E - DRAM
+			 */
+			base = 0;
+			switch (ui) {
+			case 0:
+				base |= 0xE00;
+				break;
+			case 1:
+				base |= 0xD00;
+				break;
+			case 2:
+				base |= 0xB00;
+				break;
+			case 3:
+				base |= 0x700;
+				break;
+			}
+
+			reg_write(XOR_BASE_ADDR_REG(0, cs_count), base);
+
+			/* Window x - Size - 256 MB */
+			reg_write(XOR_SIZE_MASK_REG(0, cs_count), 0x0FFF0000);
+			cs_count++;
+		}
+	}
+
+	mv_xor_hal_init(1);
+
+	return;
+}
+
+void mv_sys_xor_finish(void)
+{
+	u32 ui;
+
+	reg_write(XOR_WINDOW_CTRL_REG(0, 0), xor_regs_ctrl_backup);
+	for (ui = 0; ui < MAX_CS; ui++)
+		reg_write(XOR_BASE_ADDR_REG(0, ui), xor_regs_base_backup[ui]);
+	for (ui = 0; ui < MAX_CS; ui++)
+		reg_write(XOR_SIZE_MASK_REG(0, ui), xor_regs_mask_backup[ui]);
+
+	reg_write(XOR_ADDR_OVRD_REG(0, 0), 0);
+}
+
+/*
+ * mv_xor_hal_init - Initialize XOR engine
+ *
+ * DESCRIPTION:
+ *               This function initialize XOR unit.
+ * INPUT:
+ *       None.
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise.
+ */
+void mv_xor_hal_init(u32 chan_num)
+{
+	u32 i;
+
+	/* Abort any XOR activity & set default configuration */
+	for (i = 0; i < chan_num; i++) {
+		mv_xor_cmd_set(i, MV_STOP);
+		mv_xor_ctrl_set(i, (1 << XEXCR_REG_ACC_PROTECT_OFFS) |
+				(4 << XEXCR_DST_BURST_LIMIT_OFFS) |
+				(4 << XEXCR_SRC_BURST_LIMIT_OFFS));
+	}
+}
+
+/*
+ * mv_xor_ctrl_set - Set XOR channel control registers
+ *
+ * DESCRIPTION:
+ *
+ * INPUT:
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise.
+ * NOTE:
+ *    This function does not modify the OperationMode field of control register.
+ *
+ */
+static int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl)
+{
+	u32 val;
+
+	/* Update the XOR Engine [0..1] Configuration Registers (XExCR) */
+	val = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)))
+	    & XEXCR_OPERATION_MODE_MASK;
+	xor_ctrl &= ~XEXCR_OPERATION_MODE_MASK;
+	xor_ctrl |= val;
+	reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), xor_ctrl);
+
+	return MV_OK;
+}
+
+int mv_xor_mem_init(u32 chan, u32 start_ptr, u32 block_size, u32 init_val_high,
+		    u32 init_val_low)
+{
+	u32 tmp;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN)
+		return MV_BAD_PARAM;
+
+	if (MV_ACTIVE == mv_xor_state_get(chan))
+		return MV_BUSY;
+
+	if ((block_size < XEXBSR_BLOCK_SIZE_MIN_VALUE) ||
+	    (block_size > XEXBSR_BLOCK_SIZE_MAX_VALUE))
+		return MV_BAD_PARAM;
+
+	/* Set the operation mode to Memory Init */
+	tmp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)));
+	tmp &= ~XEXCR_OPERATION_MODE_MASK;
+	tmp |= XEXCR_OPERATION_MODE_MEM_INIT;
+	reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), tmp);
+
+	/*
+	 * Update the start_ptr field in XOR Engine [0..1] Destination Pointer
+	 * Register (XExDPR0)
+	 */
+	reg_write(XOR_DST_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)), start_ptr);
+
+	/*
+	 * Update the BlockSize field in the XOR Engine[0..1] Block Size
+	 * Registers (XExBSR)
+	 */
+	reg_write(XOR_BLOCK_SIZE_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		  block_size);
+
+	/*
+	 * Update the field InitValL in the XOR Engine Initial Value Register
+	 * Low (XEIVRL)
+	 */
+	reg_write(XOR_INIT_VAL_LOW_REG(XOR_UNIT(chan)), init_val_low);
+
+	/*
+	 * Update the field InitValH in the XOR Engine Initial Value Register
+	 * High (XEIVRH)
+	 */
+	reg_write(XOR_INIT_VAL_HIGH_REG(XOR_UNIT(chan)), init_val_high);
+
+	/* Start transfer */
+	reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		    XEXACTR_XESTART_MASK);
+
+	return MV_OK;
+}
+
+/*
+ * mv_xor_transfer - Transfer data from source to destination on one of
+ *                 three modes (XOR,CRC32,DMA)
+ *
+ * DESCRIPTION:
+ *       This function initiates XOR channel, according to function parameters,
+ *       in order to perform XOR or CRC32 or DMA transaction.
+ *       To gain maximum performance the user is asked to keep the following
+ *       restrictions:
+ *       1) Selected engine is available (not busy).
+ *       1) This module does not take into consideration CPU MMU issues.
+ *          In order for the XOR engine to access the appropreate source
+ *          and destination, address parameters must be given in system
+ *          physical mode.
+ *       2) This API does not take care of cache coherency issues. The source,
+ *          destination and in case of chain the descriptor list are assumed
+ *          to be cache coherent.
+ *       4) Parameters validity. For example, does size parameter exceeds
+ *          maximum byte count of descriptor mode (16M or 64K).
+ *
+ * INPUT:
+ *       chan          - XOR channel number. See MV_XOR_CHANNEL enumerator.
+ *       xor_type      - One of three: XOR, CRC32 and DMA operations.
+ *       xor_chain_ptr - address of chain pointer
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURS:
+ *       MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise.
+ *
+ */
+int mv_xor_transfer(u32 chan, int xor_type, u32 xor_chain_ptr)
+{
+	u32 tmp;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN) {
+		debug("%s: ERR. Invalid chan num %d\n", __func__, chan);
+		return MV_BAD_PARAM;
+	}
+
+	if (MV_ACTIVE == mv_xor_state_get(chan)) {
+		debug("%s: ERR. Channel is already active\n", __func__);
+		return MV_BUSY;
+	}
+
+	if (0x0 == xor_chain_ptr) {
+		debug("%s: ERR. xor_chain_ptr is NULL pointer\n", __func__);
+		return MV_BAD_PARAM;
+	}
+
+	/* Read configuration register and mask the operation mode field */
+	tmp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)));
+	tmp &= ~XEXCR_OPERATION_MODE_MASK;
+
+	switch (xor_type) {
+	case MV_XOR:
+		if (0 != (xor_chain_ptr & XEXDPR_DST_PTR_XOR_MASK)) {
+			debug("%s: ERR. Invalid chain pointer (bits [5:0] must be cleared)\n",
+			      __func__);
+			return MV_BAD_PARAM;
+		}
+
+		/* Set the operation mode to XOR */
+		tmp |= XEXCR_OPERATION_MODE_XOR;
+		break;
+
+	case MV_DMA:
+		if (0 != (xor_chain_ptr & XEXDPR_DST_PTR_DMA_MASK)) {
+			debug("%s: ERR. Invalid chain pointer (bits [4:0] must be cleared)\n",
+			      __func__);
+			return MV_BAD_PARAM;
+		}
+
+		/* Set the operation mode to DMA */
+		tmp |= XEXCR_OPERATION_MODE_DMA;
+		break;
+
+	case MV_CRC32:
+		if (0 != (xor_chain_ptr & XEXDPR_DST_PTR_CRC_MASK)) {
+			debug("%s: ERR. Invalid chain pointer (bits [4:0] must be cleared)\n",
+			      __func__);
+			return MV_BAD_PARAM;
+		}
+
+		/* Set the operation mode to CRC32 */
+		tmp |= XEXCR_OPERATION_MODE_CRC;
+		break;
+
+	default:
+		return MV_BAD_PARAM;
+	}
+
+	/* Write the operation mode to the register */
+	reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), tmp);
+
+	/*
+	 * Update the NextDescPtr field in the XOR Engine [0..1] Next Descriptor
+	 * Pointer Register (XExNDPR)
+	 */
+	reg_write(XOR_NEXT_DESC_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		  xor_chain_ptr);
+
+	/* Start transfer */
+	reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+		    XEXACTR_XESTART_MASK);
+
+	return MV_OK;
+}
+
+/*
+ * mv_xor_state_get - Get XOR channel state.
+ *
+ * DESCRIPTION:
+ *       XOR channel activity state can be active, idle, paused.
+ *       This function retrunes the channel activity state.
+ *
+ * INPUT:
+ *       chan     - the channel number
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       XOR_CHANNEL_IDLE    - If the engine is idle.
+ *       XOR_CHANNEL_ACTIVE  - If the engine is busy.
+ *       XOR_CHANNEL_PAUSED  - If the engine is paused.
+ *       MV_UNDEFINED_STATE  - If the engine state is undefind or there is no
+ *                             such engine
+ *
+ */
+int mv_xor_state_get(u32 chan)
+{
+	u32 state;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN) {
+		debug("%s: ERR. Invalid chan num %d\n", __func__, chan);
+		return MV_UNDEFINED_STATE;
+	}
+
+	/* Read the current state */
+	state = reg_read(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)));
+	state &= XEXACTR_XESTATUS_MASK;
+
+	/* Return the state */
+	switch (state) {
+	case XEXACTR_XESTATUS_IDLE:
+		return MV_IDLE;
+	case XEXACTR_XESTATUS_ACTIVE:
+		return MV_ACTIVE;
+	case XEXACTR_XESTATUS_PAUSED:
+		return MV_PAUSED;
+	}
+
+	return MV_UNDEFINED_STATE;
+}
+
+/*
+ * mv_xor_cmd_set - Set command of XOR channel
+ *
+ * DESCRIPTION:
+ *       XOR channel can be started, idle, paused and restarted.
+ *       Paused can be set only if channel is active.
+ *       Start can be set only if channel is idle or paused.
+ *       Restart can be set only if channel is paused.
+ *       Stop can be set only if channel is active.
+ *
+ * INPUT:
+ *       chan     - The channel number
+ *       command  - The command type (start, stop, restart, pause)
+ *
+ * OUTPUT:
+ *       None.
+ *
+ * RETURN:
+ *       MV_OK on success , MV_BAD_PARAM on erroneous parameter, MV_ERROR on
+ *       undefind XOR engine mode
+ *
+ */
+static int mv_xor_cmd_set(u32 chan, int command)
+{
+	int state;
+
+	/* Parameter checking */
+	if (chan >= MV_XOR_MAX_CHAN) {
+		debug("%s: ERR. Invalid chan num %d\n", __func__, chan);
+		return MV_BAD_PARAM;
+	}
+
+	/* Get the current state */
+	state = mv_xor_state_get(chan);
+
+	/* Command is start and current state is idle */
+	if ((command == MV_START) && (state == MV_IDLE)) {
+		reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XESTART_MASK);
+		return MV_OK;
+	}
+	/* Command is stop and current state is active */
+	else if ((command == MV_STOP) && (state == MV_ACTIVE)) {
+		reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XESTOP_MASK);
+		return MV_OK;
+	}
+	/* Command is paused and current state is active */
+	else if ((command == MV_PAUSED) && (state == MV_ACTIVE)) {
+		reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XEPAUSE_MASK);
+		return MV_OK;
+	}
+	/* Command is restart and current state is paused */
+	else if ((command == MV_RESTART) && (state == MV_PAUSED)) {
+		reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)),
+			    XEXACTR_XERESTART_MASK);
+		return MV_OK;
+	}
+	/* Command is stop and current state is active */
+	else if ((command == MV_STOP) && (state == MV_IDLE))
+		return MV_OK;
+
+	/* Illegal command */
+	debug("%s: ERR. Illegal command\n", __func__);
+
+	return MV_BAD_PARAM;
+}
diff --git a/drivers/ddr/marvell/axp/xor.h b/drivers/ddr/marvell/axp/xor.h
new file mode 100644
index 0000000..97d1056
--- /dev/null
+++ b/drivers/ddr/marvell/axp/xor.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __XOR_H
+#define __XOR_H
+
+#include "ddr3_hw_training.h"
+
+#define MV_XOR_MAX_CHAN         4 /* total channels for all units together */
+
+/*
+ * This enumerator describes the type of functionality the XOR channel
+ * can have while using the same data structures.
+ */
+enum xor_type {
+	MV_XOR,		/* XOR channel functions as XOR accelerator     */
+	MV_DMA,		/* XOR channel functions as IDMA channel        */
+	MV_CRC32	/* XOR channel functions as CRC 32 calculator   */
+};
+
+/*
+ * This enumerator describes the set of commands that can be applied on
+ * an engine (e.g. IDMA, XOR). Appling a comman depends on the current
+ * status (see MV_STATE enumerator)
+ * Start can be applied only when status is IDLE
+ * Stop can be applied only when status is IDLE, ACTIVE or PAUSED
+ * Pause can be applied only when status is ACTIVE
+ * Restart can be applied only when status is PAUSED
+ */
+enum mv_command {
+	MV_START,		/* Start     */
+	MV_STOP,		/* Stop     */
+	MV_PAUSE,		/* Pause    */
+	MV_RESTART		/* Restart  */
+};
+
+/*
+ * This enumerator describes the set of state conditions.
+ * Moving from one state to other is stricted.
+ */
+enum mv_state {
+	MV_IDLE,
+	MV_ACTIVE,
+	MV_PAUSED,
+	MV_UNDEFINED_STATE
+};
+
+/* XOR descriptor structure for CRC and DMA descriptor */
+struct crc_dma_desc {
+	u32 status;		/* Successful descriptor execution indication */
+	u32 crc32_result;	/* Result of CRC-32 calculation */
+	u32 desc_cmd;		/* type of operation to be carried out on the data */
+	u32 next_desc_ptr;	/* Next descriptor address pointer */
+	u32 byte_cnt;		/* Size of source block part represented by the descriptor */
+	u32 dst_addr;		/* Destination Block address pointer (not used in CRC32 */
+	u32 src_addr0;		/* Mode: Source Block address pointer */
+	u32 src_addr1;		/* Mode: Source Block address pointer */
+} __packed;
+
+void mv_xor_hal_init(u32 chan_num);
+int mv_xor_state_get(u32 chan);
+void mv_sys_xor_init(MV_DRAM_INFO *dram_info);
+void mv_sys_xor_finish(void);
+int mv_xor_transfer(u32 chan, int xor_type, u32 xor_chain_ptr);
+int mv_xor_mem_init(u32 chan, u32 start_ptr, u32 block_size, u32 init_val_high,
+		    u32 init_val_low);
+
+#endif /* __XOR_H */
diff --git a/drivers/ddr/marvell/axp/xor_regs.h b/drivers/ddr/marvell/axp/xor_regs.h
new file mode 100644
index 0000000..db5c419
--- /dev/null
+++ b/drivers/ddr/marvell/axp/xor_regs.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ */
+
+#ifndef __XOR_REGS_H
+#define __XOR_REGS_H
+
+/*
+ * For controllers that have two XOR units, then chans 2 & 3 will be mapped
+ * to channels 0 & 1 of unit 1
+ */
+#define XOR_UNIT(chan)			((chan) >> 1)
+#define XOR_CHAN(chan)			((chan) & 1)
+
+#define MV_XOR_REGS_OFFSET(unit)	(0x60900)
+#define MV_XOR_REGS_BASE(unit)		(MV_XOR_REGS_OFFSET(unit))
+
+/* XOR Engine Control Register Map */
+#define XOR_CHANNEL_ARBITER_REG(unit)	(MV_XOR_REGS_BASE(unit))
+#define XOR_CONFIG_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + (0x10 + ((chan) * 4)))
+#define XOR_ACTIVATION_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + (0x20 + ((chan) * 4)))
+
+/* XOR Engine Interrupt Register Map */
+#define XOR_CAUSE_REG(unit)		(MV_XOR_REGS_BASE(unit) + 0x30)
+#define XOR_MASK_REG(unit)		(MV_XOR_REGS_BASE(unit) + 0x40)
+#define XOR_ERROR_CAUSE_REG(unit)	(MV_XOR_REGS_BASE(unit) + 0x50)
+#define XOR_ERROR_ADDR_REG(unit)	(MV_XOR_REGS_BASE(unit) + 0x60)
+
+/* XOR Engine Descriptor Register Map */
+#define XOR_NEXT_DESC_PTR_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + (0x200 + ((chan) * 4)))
+#define XOR_CURR_DESC_PTR_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + (0x210 + ((chan) * 4)))
+#define XOR_BYTE_COUNT_REG(unit, chan)		(MV_XOR_REGS_BASE(unit) + (0x220 + ((chan) * 4)))
+
+#define XOR_DST_PTR_REG(unit, chan)		(MV_XOR_REGS_BASE(unit) + (0x2B0 + ((chan) * 4)))
+#define XOR_BLOCK_SIZE_REG(unit, chan)		(MV_XOR_REGS_BASE(unit) + (0x2C0 + ((chan) * 4)))
+#define XOR_TIMER_MODE_CTRL_REG(unit)		(MV_XOR_REGS_BASE(unit) + 0x2D0)
+#define XOR_TIMER_MODE_INIT_VAL_REG(unit)	(MV_XOR_REGS_BASE(unit) + 0x2D4)
+#define XOR_TIMER_MODE_CURR_VAL_REG(unit)	(MV_XOR_REGS_BASE(unit) + 0x2D8)
+#define XOR_INIT_VAL_LOW_REG(unit)		(MV_XOR_REGS_BASE(unit) + 0x2E0)
+#define XOR_INIT_VAL_HIGH_REG(unit)		(MV_XOR_REGS_BASE(unit) + 0x2E4)
+
+/* XOR register fileds */
+
+/* XOR Engine [0..1] Configuration Registers (XExCR) */
+#define XEXCR_OPERATION_MODE_OFFS	(0)
+#define XEXCR_OPERATION_MODE_MASK	(7 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_XOR	(0 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_CRC	(1 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_DMA	(2 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_ECC	(3 << XEXCR_OPERATION_MODE_OFFS)
+#define XEXCR_OPERATION_MODE_MEM_INIT	(4 << XEXCR_OPERATION_MODE_OFFS)
+
+#define XEXCR_SRC_BURST_LIMIT_OFFS	(4)
+#define XEXCR_SRC_BURST_LIMIT_MASK	(7 << XEXCR_SRC_BURST_LIMIT_OFFS)
+#define XEXCR_DST_BURST_LIMIT_OFFS	(8)
+#define XEXCR_DST_BURST_LIMIT_MASK	(7 << XEXCR_DST_BURST_LIMIT_OFFS)
+#define XEXCR_DRD_RES_SWP_OFFS		(12)
+#define XEXCR_DRD_RES_SWP_MASK		(1 << XEXCR_DRD_RES_SWP_OFFS)
+#define XEXCR_DWR_REQ_SWP_OFFS		(13)
+#define XEXCR_DWR_REQ_SWP_MASK		(1 << XEXCR_DWR_REQ_SWP_OFFS)
+#define XEXCR_DES_SWP_OFFS		(14)
+#define XEXCR_DES_SWP_MASK		(1 << XEXCR_DES_SWP_OFFS)
+#define XEXCR_REG_ACC_PROTECT_OFFS	(15)
+#define XEXCR_REG_ACC_PROTECT_MASK	(1 << XEXCR_REG_ACC_PROTECT_OFFS)
+
+/* XOR Engine [0..1] Activation Registers (XExACTR) */
+#define XEXACTR_XESTART_OFFS		(0)
+#define XEXACTR_XESTART_MASK		(1 << XEXACTR_XESTART_OFFS)
+#define XEXACTR_XESTOP_OFFS		(1)
+#define XEXACTR_XESTOP_MASK		(1 << XEXACTR_XESTOP_OFFS)
+#define XEXACTR_XEPAUSE_OFFS		(2)
+#define XEXACTR_XEPAUSE_MASK		(1 << XEXACTR_XEPAUSE_OFFS)
+#define XEXACTR_XERESTART_OFFS		(3)
+#define XEXACTR_XERESTART_MASK		(1 << XEXACTR_XERESTART_OFFS)
+#define XEXACTR_XESTATUS_OFFS		(4)
+#define XEXACTR_XESTATUS_MASK		(3 << XEXACTR_XESTATUS_OFFS)
+#define XEXACTR_XESTATUS_IDLE		(0 << XEXACTR_XESTATUS_OFFS)
+#define XEXACTR_XESTATUS_ACTIVE		(1 << XEXACTR_XESTATUS_OFFS)
+#define XEXACTR_XESTATUS_PAUSED		(2 << XEXACTR_XESTATUS_OFFS)
+
+/* XOR Engine [0..1] Destination Pointer Register (XExDPR0) */
+#define XEXDPR_DST_PTR_OFFS		(0)
+#define XEXDPR_DST_PTR_MASK		(0xFFFFFFFF << XEXDPR_DST_PTR_OFFS)
+#define XEXDPR_DST_PTR_XOR_MASK		(0x3F)
+#define XEXDPR_DST_PTR_DMA_MASK		(0x1F)
+#define XEXDPR_DST_PTR_CRC_MASK		(0x1F)
+
+/* XOR Engine[0..1] Block Size Registers (XExBSR) */
+#define XEXBSR_BLOCK_SIZE_OFFS		(0)
+#define XEXBSR_BLOCK_SIZE_MASK		(0xFFFFFFFF << XEXBSR_BLOCK_SIZE_OFFS)
+#define XEXBSR_BLOCK_SIZE_MIN_VALUE	(128)
+#define XEXBSR_BLOCK_SIZE_MAX_VALUE	(0xFFFFFFFF)
+
+/* XOR Engine Address Decoding Register Map */
+#define XOR_WINDOW_CTRL_REG(unit, chan)	(MV_XOR_REGS_BASE(unit) + (0x240 + ((chan) * 4)))
+#define XOR_BASE_ADDR_REG(unit, win)	(MV_XOR_REGS_BASE(unit) + (0x250 + ((win) * 4)))
+#define XOR_SIZE_MASK_REG(unit, win)	(MV_XOR_REGS_BASE(unit) + (0x270 + ((win) * 4)))
+#define XOR_HIGH_ADDR_REMAP_REG(unit, win) (MV_XOR_REGS_BASE(unit) + (0x290 + ((win) * 4)))
+#define XOR_ADDR_OVRD_REG(unit, win)	(MV_XOR_REGS_BASE(unit) + (0x2A0 + ((win) * 4)))
+
+#endif /* __XOR_REGS_H */