mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 15:37:23 +00:00
1375 lines
38 KiB
C
1375 lines
38 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|