/*
 * 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_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
