arm: mvebu: drivers/ddr: Move Armada XP DDR init code into new directory
[oweals/u-boot.git] / drivers / ddr / marvell / axp / ddr3_dqs.c
diff --git a/drivers/ddr/marvell/axp/ddr3_dqs.c b/drivers/ddr/marvell/axp/ddr3_dqs.c
new file mode 100644 (file)
index 0000000..71a986d
--- /dev/null
@@ -0,0 +1,1374 @@
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+
+#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;
+}