4373 lines
124 KiB
C
Executable File
4373 lines
124 KiB
C
Executable File
/*
|
|
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
|
|
*/
|
|
|
|
#include "ddr_training_impl.h"
|
|
#include "ddr_interface.h"
|
|
#include <stdint.h>
|
|
|
|
#define __common__
|
|
|
|
unsigned int ddr_read(unsigned addr)
|
|
{
|
|
return (*(volatile unsigned int *)((uintptr_t)(addr)));
|
|
}
|
|
|
|
void ddr_write(unsigned val, unsigned addr)
|
|
{
|
|
(*(volatile unsigned int *)((uintptr_t)(addr))) = (val);
|
|
}
|
|
|
|
void* ddrtr_memcpy(void *dst, const void *src, unsigned int len)
|
|
{
|
|
const char *s = src;
|
|
char *d = dst;
|
|
|
|
while (len--)
|
|
*d++ = *s++;
|
|
return dst;
|
|
}
|
|
|
|
void* ddrtr_memset(void *b, int c, unsigned int len)
|
|
{
|
|
char *bp = b;
|
|
|
|
while (len--)
|
|
*bp++ = (unsigned char)c;
|
|
return b;
|
|
}
|
|
|
|
int ddr_training_by_dmc(struct ddr_cfg_st *cfg)
|
|
{
|
|
if (cfg->cmd_st) {
|
|
#ifdef DDR_TRAINING_CMD
|
|
return ddr_training_cmd_func(cfg);
|
|
#endif
|
|
} else {
|
|
return ddr_training_boot_func(cfg);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ddr_training_by_rank(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
|
|
DDR_PHY_SWITCH_RANK(cfg->cur_phy, cfg->rank_idx);
|
|
|
|
for (i = 0; i < cfg->phy[cfg->phy_idx].dmc_num; i++) {
|
|
cfg->dmc_idx = i;
|
|
cfg->cur_dmc = cfg->phy[cfg->phy_idx].dmc[i].addr;
|
|
cfg->cur_pattern = cfg->phy[cfg->phy_idx].dmc[i].ddrt_pattern;
|
|
result += ddr_training_by_dmc(cfg);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ddr_training_by_phy(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
unsigned int phy_mask = 1 << (cfg->phy_idx);
|
|
unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num;
|
|
for (i = 0; i < rank_num; i++) {
|
|
cfg->rank_idx = i;
|
|
cfg->cur_item = cfg->phy[cfg->phy_idx].rank[i].item;
|
|
if (ddr_training_check_bypass(cfg, phy_mask))
|
|
continue;
|
|
result += ddr_training_by_rank(cfg);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ddr_training_all(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
for (i = 0; i < cfg->phy_num; i++) {
|
|
cfg->phy_idx = i;
|
|
cfg->cur_phy = cfg->phy[i].addr;
|
|
result += ddr_training_by_phy(cfg);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* DDR training phy/dmc/dram_type config init */
|
|
void ddr_training_cfg_set_dmc(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int ddrt_pattern;
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[0].dram_type) {
|
|
cfg->phy[0].dmc_num = 2;
|
|
ddrt_pattern = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN);
|
|
cfg->phy[0].dmc[0].addr = DDR_REG_BASE_DMC0;
|
|
cfg->phy[0].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff;
|
|
cfg->phy[0].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC0);
|
|
cfg->phy[0].dmc[1].addr = DDR_REG_BASE_DMC1;
|
|
cfg->phy[0].dmc[1].ddrt_pattern = ddrt_pattern >> 16;
|
|
cfg->phy[0].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC1);
|
|
cfg->phy[0].total_byte_num = cfg->phy[0].dmc[0].byte_num + cfg->phy[0].dmc[1].byte_num;
|
|
} else {
|
|
cfg->phy[0].dmc_num = 1;
|
|
cfg->phy[0].dmc[0].addr = DDR_REG_BASE_DMC0;
|
|
cfg->phy[0].dmc[0].ddrt_pattern = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN);
|
|
cfg->phy[0].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC0);
|
|
cfg->phy[0].total_byte_num = cfg->phy[0].dmc[0].byte_num;
|
|
}
|
|
DDR_INFO("phy[0] total_byte_num[%x] dram_type[%x]", cfg->phy[0].total_byte_num, cfg->phy[0].dram_type);
|
|
|
|
#ifdef DDR_REG_BASE_PHY1
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[1].dram_type) {
|
|
cfg->phy[1].dmc_num = 2;
|
|
ddrt_pattern = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_SEC);
|
|
cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC2;
|
|
cfg->phy[1].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff;
|
|
cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC2);
|
|
cfg->phy[1].dmc[1].addr = DDR_REG_BASE_DMC3;
|
|
cfg->phy[1].dmc[1].ddrt_pattern = ddrt_pattern >> 16;
|
|
cfg->phy[1].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC3);
|
|
cfg->phy[1].total_byte_num = cfg->phy[1].dmc[0].byte_num + cfg->phy[1].dmc[1].byte_num;
|
|
} else {
|
|
cfg->phy[1].dmc_num = 1;
|
|
cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC1;
|
|
cfg->phy[1].dmc[0].ddrt_pattern = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_SEC);
|
|
cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC1);
|
|
cfg->phy[1].total_byte_num = cfg->phy[1].dmc[0].byte_num;
|
|
}
|
|
DDR_INFO("phy[1] total_byte_num[%x] dram_type[%x]", cfg->phy[1].total_byte_num, cfg->phy[1].dram_type);
|
|
#endif
|
|
}
|
|
void ddr_training_cfg_set_rank(struct ddr_cfg_st *cfg)
|
|
{
|
|
cfg->phy[0].rank_num = 1;
|
|
cfg->phy[0].rank[0].item = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG);
|
|
cfg->phy[0].rank[0].item_hw = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0);
|
|
|
|
cfg->phy[0].rank[1].item = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC);
|
|
cfg->phy[0].rank[1].item_hw = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1);
|
|
|
|
if (ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1)) {
|
|
cfg->phy[0].rank_num = 2;
|
|
}
|
|
|
|
DDR_INFO("Rank number PHY0 [%x]", cfg->phy[0].rank_num);
|
|
DDR_INFO("HW training item PHY0[%x = %x][%x = %x]",
|
|
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0), cfg->phy[0].rank[0].item_hw,
|
|
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1), cfg->phy[0].rank[1].item_hw);
|
|
|
|
#ifdef DDR_REG_BASE_PHY1
|
|
cfg->phy[1].rank_num = 1;
|
|
cfg->phy[1].rank[0].item = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG);
|
|
cfg->phy[1].rank[0].item_hw = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0);
|
|
|
|
cfg->phy[1].rank[1].item = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC);
|
|
cfg->phy[1].rank[1].item_hw = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1);
|
|
|
|
|
|
if (ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1)) {
|
|
cfg->phy[1].rank_num = 2;
|
|
}
|
|
|
|
DDR_INFO("Rank number PHY1[%x]", cfg->phy[1].rank_num);
|
|
DDR_INFO("HW training item PHY1[%x = %x][%x = %x]",
|
|
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0), cfg->phy[1].rank[0].item_hw,
|
|
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1), cfg->phy[1].rank[1].item_hw);
|
|
#endif
|
|
|
|
DDR_INFO("SW training item Rank0[%x = %x] Rank1[%x = %x]",
|
|
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), cfg->phy[0].rank[0].item,
|
|
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), cfg->phy[0].rank[1].item);
|
|
}
|
|
|
|
void ddr_training_cfg_set_phy(struct ddr_cfg_st *cfg)
|
|
{
|
|
cfg->phy_num = DDR_PHY_NUM;
|
|
cfg->phy[0].addr = DDR_REG_BASE_PHY0;
|
|
cfg->phy[0].dram_type = ddr_read(DDR_REG_BASE_PHY0 + DDR_PHY_DRAMCFG)
|
|
& PHY_DRAMCFG_TYPE_MASK;
|
|
#ifdef DDR_REG_BASE_PHY1
|
|
cfg->phy[1].addr = DDR_REG_BASE_PHY1;
|
|
cfg->phy[1].dram_type = ddr_read(DDR_REG_BASE_PHY1 + DDR_PHY_DRAMCFG)
|
|
& PHY_DRAMCFG_TYPE_MASK;
|
|
#endif
|
|
}
|
|
|
|
void ddr_training_cfg_init(struct ddr_cfg_st *cfg)
|
|
{
|
|
ddrtr_memset(cfg, 0, sizeof(struct ddr_cfg_st));
|
|
ddr_training_cfg_set_phy(cfg);
|
|
ddr_training_cfg_set_dmc(cfg);
|
|
ddr_training_cfg_set_rank(cfg);
|
|
}
|
|
|
|
/* 2GHz CPU run 2000 "nop" in 1 ns */
|
|
static inline void ddr_training_delay(unsigned int cnt)
|
|
{
|
|
while (cnt--)
|
|
asm("nop");
|
|
}
|
|
|
|
/* set auto refresh */
|
|
void ddr_training_set_timing(unsigned int base_dmc, unsigned int timing)
|
|
{
|
|
ddr_training_delay(DDR_AUTO_TIMING_DELAY);
|
|
ddr_write(timing, base_dmc + DDR_DMC_TIMING2);
|
|
/* need to delay 1 ns */
|
|
ddr_training_delay(DDR_AUTO_TIMING_DELAY);
|
|
}
|
|
|
|
#ifdef DDR_TRAINING_STAT_CONFIG
|
|
/* Save training result in stat register */
|
|
static void ddr_training_save(unsigned int mask, unsigned int phy,
|
|
int byte, int dq)
|
|
{
|
|
unsigned int stat;
|
|
unsigned int phy_index;
|
|
|
|
stat = ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT);
|
|
/* only record the first error */
|
|
if (stat)
|
|
return;
|
|
|
|
stat = mask;
|
|
|
|
if (0 != phy) {
|
|
phy_index = (DDR_REG_BASE_PHY0 == phy ?
|
|
DDR_ERR_PHY0 : DDR_ERR_PHY1);
|
|
stat |= phy_index;
|
|
}
|
|
|
|
if (-1 != byte)
|
|
stat |= ((unsigned int)byte << DDR_ERR_BYTE_BIT);
|
|
|
|
if (-1 != dq)
|
|
stat |= ((unsigned int)dq << DDR_ERR_DQ_BIT);
|
|
|
|
ddr_write(stat, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT);
|
|
}
|
|
#endif
|
|
|
|
/* Record error code in register */
|
|
void ddr_training_stat(unsigned int mask, unsigned int phy, int byte, int dq)
|
|
{
|
|
ddr_training_error(mask, phy, byte, dq);
|
|
#ifdef DDR_TRAINING_STAT_CONFIG
|
|
ddr_training_save(mask, phy, byte, dq);
|
|
#endif
|
|
}
|
|
|
|
/* Check DDR training item whether by pass */
|
|
int ddr_training_check_bypass(struct ddr_cfg_st *cfg, unsigned int mask)
|
|
{
|
|
/* training item disable */
|
|
if ((cfg->cur_item) & mask) {
|
|
DDR_DEBUG("DDR training [%x] is disable, rank[%x] cfg[%x]",
|
|
mask, cfg->rank_idx, cfg->cur_item);
|
|
return DDR_TRUE;
|
|
} else {
|
|
return DDR_FALSE;
|
|
}
|
|
}
|
|
|
|
#if !defined(DDR_TRAINING_CUT_CODE_CONFIG) || defined(DDR_TRAINING_CMD)
|
|
/**
|
|
* Check PHY whether disable.
|
|
* DDR_TRUE: PHY is disable.
|
|
* DDR_FALSE: PHY is not disable.
|
|
*/
|
|
int ddr_training_phy_disable(int index)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Save register value before training */
|
|
void ddr_training_save_reg(struct ddr_cfg_st *cfg,
|
|
struct tr_relate_reg *relate_reg, unsigned int mask)
|
|
{
|
|
unsigned int base_dmc = cfg->cur_dmc;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
/* save reg value */
|
|
relate_reg->auto_ref_timing =
|
|
ddr_read(base_dmc + DDR_DMC_TIMING2);
|
|
relate_reg->power_down =
|
|
ddr_read(base_dmc + DDR_DMC_CFG_PD);
|
|
relate_reg->misc_scramb = ddr_read(base_phy + DDR_PHY_MISC);
|
|
/* Static register have to read two times to get the right value. */
|
|
relate_reg->ac_phy_ctl =
|
|
ddr_read(base_phy + DDR_PHY_ACPHYCTL4);
|
|
relate_reg->ac_phy_ctl =
|
|
ddr_read(base_phy + DDR_PHY_ACPHYCTL4);
|
|
|
|
/* set new value */
|
|
switch (mask) {
|
|
case DDR_BYPASS_WL_MASK:
|
|
case DDR_BYPASS_LPCA_MASK:
|
|
/* disable auto refresh */
|
|
ddr_training_set_timing(base_dmc,
|
|
relate_reg->auto_ref_timing & DMC_AUTO_TIMING_DIS);
|
|
break;
|
|
case DDR_BYPASS_GATE_MASK:
|
|
/* disable auto refresh */
|
|
ddr_training_set_timing(base_dmc,
|
|
relate_reg->auto_ref_timing & DMC_AUTO_TIMING_DIS);
|
|
|
|
if (!(ddr_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) /* set 1T */
|
|
ddr_write(0x0, base_phy + DDR_PHY_ACPHYCTL4);
|
|
break;
|
|
case DDR_BYPASS_HW_MASK:
|
|
if (!(ddr_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) /* set 1T */
|
|
ddr_write(0x0, base_phy + DDR_PHY_ACPHYCTL4);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ddr_write(relate_reg->power_down & DMC_POWER_DOWN_DIS,
|
|
base_dmc + DDR_DMC_CFG_PD);
|
|
ddr_write(relate_reg->misc_scramb & PHY_MISC_SCRAMB_DIS,
|
|
base_phy + DDR_PHY_MISC);
|
|
|
|
DDR_DQSSWAP_SAVE_FUNC(relate_reg->swapdfibyte_en, base_phy);
|
|
|
|
DDR_AXI_SAVE_FUNC(relate_reg);
|
|
|
|
DDR_RNKVOL_SAVE_FUNC(relate_reg, base_dmc);
|
|
|
|
/* save customer reg */
|
|
DDR_TRAINING_SAVE_REG_FUNC((void *)relate_reg, mask);
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
DDR_ASM_DSB();
|
|
}
|
|
|
|
/* Restore register value after training */
|
|
void ddr_training_restore_reg(struct ddr_cfg_st *cfg,
|
|
struct tr_relate_reg *relate_reg)
|
|
{
|
|
unsigned int base_dmc = cfg->cur_dmc;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
/* enable auto refresh */
|
|
ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing);
|
|
ddr_write(relate_reg->power_down, base_dmc + DDR_DMC_CFG_PD);
|
|
ddr_write(relate_reg->misc_scramb, base_phy + DDR_PHY_MISC);
|
|
if (!(ddr_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T))
|
|
ddr_write(relate_reg->ac_phy_ctl, base_phy + DDR_PHY_ACPHYCTL4);
|
|
|
|
DDR_DQSSWAP_RESTORE_FUNC(relate_reg->swapdfibyte_en, base_phy);
|
|
|
|
DDR_AXI_RESTORE_FUNC(relate_reg);
|
|
|
|
DDR_RNKVOL_RESTORE_FUNC(relate_reg, base_dmc);
|
|
|
|
/* restore customer reg */
|
|
DDR_TRAINING_RESTORE_REG_FUNC((void *)relate_reg);
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
DDR_ASM_DSB();
|
|
}
|
|
|
|
/* Switch AXI to DMC0/DMC1/DMC2/DMC3 for DDRT test */
|
|
void ddr_training_switch_axi(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_AXI_SWITCH_FUNC(cfg);
|
|
|
|
DDR_RNKVOL_SET_FUNC(cfg);
|
|
}
|
|
#endif
|
|
|
|
#if defined(DDR_WL_TRAINING_CONFIG) || defined(DDR_MPR_TRAINING_CONFIG)
|
|
|
|
/* Excute DMC sfc command */
|
|
static void ddr_dmc_sfc_cmd(unsigned int base_dmc, unsigned int sfc_cmd,
|
|
unsigned int sfc_addr, unsigned int sfc_bank)
|
|
{
|
|
unsigned int count = 0;
|
|
|
|
/* set sfc cmd */
|
|
DMC_SFC_CMD_WRITE(sfc_cmd, base_dmc + DDR_DMC_SFCCMD);
|
|
/* set col and row */
|
|
ddr_write(sfc_addr, base_dmc + DDR_DMC_SFCADDR);
|
|
/* set bank */
|
|
DMC_SFC_BANK_WRITE(sfc_bank, base_dmc + DDR_DMC_SFCBANK);
|
|
/* excute cmd */
|
|
ddr_write(0x1, base_dmc + DDR_DMC_SFCREQ);
|
|
|
|
DDR_ASM_DSB();
|
|
|
|
while (count < DDR_SFC_WAIT_TIMEOUT) { /* wait command finished */
|
|
if (!(ddr_read(base_dmc + DDR_DMC_SFCREQ) & 0x1))
|
|
break;
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count >= DDR_HWR_WAIT_TIMEOUT)
|
|
DDR_ERROR("SFC cmd wait timeout.");
|
|
}
|
|
#endif
|
|
|
|
#if defined(DDR_HW_TRAINING_CONFIG) || defined(DDR_DCC_TRAINING_CONFIG)
|
|
|
|
/* Exit or enter auto self-refresh */
|
|
static int ddr_training_easr(unsigned int base_dmc, unsigned int sref_req)
|
|
{
|
|
unsigned int count = DDR_HWR_WAIT_TIMEOUT;
|
|
if (DDR_EXIT_SREF == sref_req) {
|
|
/* Exit Auto-self refresh */
|
|
ddr_write(DMC_CTRL_SREF_EXIT, base_dmc + DDR_DMC_CTRL_SREF);
|
|
|
|
while (count--) {
|
|
if (!(ddr_read(base_dmc + DDR_DMC_CURR_FUNC)
|
|
& DMC_CURR_FUNC_IN_SREF_MASK))
|
|
break;
|
|
}
|
|
} else if (DDR_ENTER_SREF == sref_req) {
|
|
/* Enter Auto-self refresh */
|
|
ddr_write(DMC_CTRL_SREF_ENTER, base_dmc + DDR_DMC_CTRL_SREF);
|
|
|
|
while (count--) {
|
|
if (ddr_read(base_dmc + DDR_DMC_CURR_FUNC)
|
|
& DMC_CURR_FUNC_IN_SREF_MASK)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count == 0xffffffff) {
|
|
DDR_FATAL("SREF wait timeout.");
|
|
ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, -1, -1, -1);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* DDR hw/dcc training exit or enter auto self-refresh */
|
|
static int ddr_training_ctrl_easr(struct ddr_cfg_st *cfg, unsigned int sref_req)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx];
|
|
|
|
for (i = 0; i < phy_st->dmc_num; i++) {
|
|
result += ddr_training_easr(phy_st->dmc[i].addr, sref_req);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void ddr_training_save_timing(struct ddr_cfg_st *cfg, struct ddr_timing_st *timing_st)
|
|
{
|
|
int i;
|
|
struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx];
|
|
|
|
for (i = 0; i < phy_st->dmc_num; i++) {
|
|
|
|
timing_st->val[i] = ddr_read(phy_st->dmc[i].addr + DDR_DMC_TIMING2);
|
|
/* disable auto refresh */
|
|
ddr_training_set_timing(phy_st->dmc[i].addr, timing_st->val[i] & DMC_AUTO_TIMING_DIS);
|
|
}
|
|
}
|
|
|
|
static void ddr_training_restore_timing(struct ddr_cfg_st *cfg, struct ddr_timing_st *timing_st)
|
|
{
|
|
int i;
|
|
struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx];
|
|
for (i = 0; i < phy_st->dmc_num; i++) {
|
|
ddr_training_set_timing(phy_st->dmc[i].addr, timing_st->val[i]);
|
|
}
|
|
}
|
|
#endif /* DDR_HW_TRAINING_CONFIG || DDR_DCC_TRAINING_CONFIG*/
|
|
|
|
/**
|
|
* Update delay setting in registers to PHY immediately.
|
|
* Make delay setting take effect.
|
|
*/
|
|
void ddr_phy_cfg_update(unsigned int base_phy)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
tmp = ddr_read(base_phy + DDR_PHY_MISC);
|
|
tmp |= (1 << PHY_MISC_UPDATE_BIT);
|
|
/* update new config to PHY */
|
|
ddr_write(tmp, base_phy + DDR_PHY_MISC);
|
|
tmp &= ~(1 << PHY_MISC_UPDATE_BIT);
|
|
ddr_write(tmp, base_phy + DDR_PHY_MISC);
|
|
tmp = ddr_read(base_phy + DDR_PHY_PHYINITCTRL);
|
|
/* set 1 to issue PHY counter reset signal */
|
|
tmp |= (1 << PHY_PHYCONN_RST_BIT);
|
|
ddr_write(tmp, base_phy + DDR_PHY_PHYINITCTRL);
|
|
/* set 0 to end the reset signal */
|
|
tmp &= ~(1 << PHY_PHYCONN_RST_BIT);
|
|
ddr_write(tmp, base_phy + DDR_PHY_PHYINITCTRL);
|
|
|
|
DDR_ASM_DSB();
|
|
}
|
|
|
|
/* Set delay value of the bit delay line of the DATA block */
|
|
void ddr_phy_set_dq_bdl(struct ddr_cfg_st *cfg, unsigned int value)
|
|
{
|
|
unsigned int val;
|
|
unsigned int offset;
|
|
unsigned int dq;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_index = cfg->cur_byte;
|
|
unsigned int rank = cfg->rank_idx;
|
|
|
|
dq = cfg->cur_dq & 0x7;
|
|
if (DDR_MODE_WRITE == cfg->cur_mode) {
|
|
if (dq < 4)
|
|
offset = DDR_PHY_DXNWDQNBDL0(rank, byte_index);
|
|
else
|
|
offset = DDR_PHY_DXNWDQNBDL1(rank, byte_index);
|
|
} else {
|
|
if (dq < 4)
|
|
offset = DDR_PHY_DXNRDQNBDL0(rank, byte_index);
|
|
else
|
|
offset = DDR_PHY_DXNRDQNBDL1(rank, byte_index);
|
|
}
|
|
|
|
dq &= 0x3;
|
|
val = ddr_read(base_phy + offset);
|
|
val &= ~(0xFF << (dq << 3));
|
|
val |= ((PHY_BDL_MASK & value) << ((dq << 3) + PHY_BDL_DQ_BIT));
|
|
ddr_write(val, base_phy + offset);
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
}
|
|
|
|
/* Get PHY DQ value */
|
|
unsigned int ddr_phy_get_dq_bdl(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int val;
|
|
unsigned int offset;
|
|
unsigned int dq;
|
|
unsigned int byte_index = cfg->cur_byte;
|
|
unsigned int rank = cfg->rank_idx;
|
|
|
|
dq = cfg->cur_dq & 0x7;
|
|
if (DDR_MODE_WRITE == cfg->cur_mode) {
|
|
if (dq < 4)
|
|
offset = DDR_PHY_DXNWDQNBDL0(rank, byte_index);
|
|
else
|
|
offset = DDR_PHY_DXNWDQNBDL1(rank, byte_index);
|
|
} else {
|
|
if (dq < 4)
|
|
offset = DDR_PHY_DXNRDQNBDL0(rank, byte_index);
|
|
else
|
|
offset = DDR_PHY_DXNRDQNBDL1(rank, byte_index);
|
|
}
|
|
|
|
dq &= 0x3;
|
|
val = (ddr_read(cfg->cur_phy + offset) >> ((dq << 3) + PHY_BDL_DQ_BIT)) & PHY_BDL_MASK;
|
|
|
|
return val;
|
|
}
|
|
|
|
/* Get byte number */
|
|
unsigned int ddr_phy_get_byte_num(unsigned int base_dmc)
|
|
{
|
|
unsigned int byte_num;
|
|
|
|
/* memery width -> byte number */
|
|
byte_num = ((ddr_read(base_dmc + DDR_DMC_CFG_DDRMODE)
|
|
>> DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK) << 1;
|
|
|
|
/* for codedex */
|
|
if (byte_num > DDR_PHY_BYTE_MAX) {
|
|
byte_num = DDR_PHY_BYTE_MAX;
|
|
DDR_ERROR("get byte num fail");
|
|
}
|
|
|
|
return byte_num;
|
|
}
|
|
|
|
|
|
static void ddr_rdqs_sync_rdm(struct ddr_cfg_st *cfg, int offset)
|
|
{
|
|
unsigned int rdqnbdl;
|
|
int rdm;
|
|
|
|
rdqnbdl = ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQNBDL2(cfg->rank_idx, cfg->cur_byte));
|
|
rdm = (rdqnbdl >> PHY_RDM_BDL_BIT) & PHY_RDM_BDL_MASK;
|
|
rdm += offset;
|
|
rdm = (rdm < 0 ? 0 : rdm);
|
|
rdm = (rdm > PHY_RDM_BDL_MASK ? PHY_RDM_BDL_MASK : rdm);
|
|
rdqnbdl = rdqnbdl & (~(PHY_RDM_BDL_MASK << PHY_RDM_BDL_BIT));
|
|
ddr_write(rdqnbdl | ((unsigned int)rdm << PHY_RDM_BDL_BIT), cfg->cur_phy + DDR_PHY_DXNRDQNBDL2(cfg->rank_idx, cfg->cur_byte));
|
|
}
|
|
|
|
static void ddr_rdqs_sync_rank_rdq(struct ddr_cfg_st *cfg, int offset)
|
|
{
|
|
int dq_val;
|
|
int i;
|
|
|
|
/* sync other rank rdm */
|
|
ddr_rdqs_sync_rdm(cfg, offset);
|
|
|
|
/* sync other rank rdq */
|
|
DDR_DEBUG("Before sync rank[%x] byte[%x] dq[%x = %x][%x = %x] offset[%x]",
|
|
cfg->rank_idx, cfg->cur_byte,
|
|
cfg->cur_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, cfg->cur_byte),
|
|
ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, cfg->cur_byte)),
|
|
cfg->cur_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, cfg->cur_byte),
|
|
ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, cfg->cur_byte)), offset);
|
|
|
|
for (i = 0; i < DDR_PHY_BIT_NUM; i++) {
|
|
cfg->cur_dq = i;
|
|
dq_val = (int)ddr_phy_get_dq_bdl(cfg);
|
|
dq_val += offset;
|
|
dq_val = (dq_val < 0 ? 0 : dq_val);
|
|
dq_val = (dq_val > PHY_BDL_MASK ? PHY_BDL_MASK : dq_val);
|
|
ddr_phy_set_dq_bdl(cfg, dq_val);
|
|
}
|
|
|
|
DDR_DEBUG("After sync rank[%x] byte[%x] dq[%x = %x][%x = %x]",
|
|
cfg->rank_idx, cfg->cur_byte,
|
|
cfg->cur_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, cfg->cur_byte),
|
|
ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, cfg->cur_byte)),
|
|
cfg->cur_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, cfg->cur_byte),
|
|
ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, cfg->cur_byte)));
|
|
}
|
|
|
|
static void ddr_bdl_adj(struct ddr_cfg_st *cfg)
|
|
{
|
|
int i;
|
|
int value_num = 10;
|
|
unsigned int rank = cfg->rank_idx;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_idx = cfg->cur_byte;
|
|
unsigned int bdl[value_num];
|
|
unsigned int min = 0xffffffff;
|
|
unsigned int dq03, dq47, rdm, rdqs;
|
|
|
|
dq03 = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL0(rank, byte_idx));
|
|
dq47 = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL1(rank, byte_idx));
|
|
rdm = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL2(rank, byte_idx));
|
|
rdqs = ddr_read(base_phy + DDR_PHY_DXNRDQSDLY(byte_idx));
|
|
|
|
bdl[0] = (dq03 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK;
|
|
bdl[1] = (dq03 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK;
|
|
bdl[2] = (dq03 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK;
|
|
bdl[3] = (dq03 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK;
|
|
bdl[4] = (dq47 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK;
|
|
bdl[5] = (dq47 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK;
|
|
bdl[6] = (dq47 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK;
|
|
bdl[7] = (dq47 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK;
|
|
bdl[8] = (rdm >> PHY_RDM_BDL_BIT) & PHY_RDM_BDL_MASK;
|
|
bdl[9] = (rdqs >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK;
|
|
|
|
for (i = 0; i < value_num; i++) {
|
|
if (bdl[i] < min)
|
|
min = bdl[i];
|
|
}
|
|
|
|
dq03 = ((bdl[0] - min) << PHY_BDL_DQ0_BIT) | ((bdl[1] - min) << PHY_BDL_DQ1_BIT) |
|
|
((bdl[2] - min) << PHY_BDL_DQ2_BIT) | ((bdl[3] - min) << PHY_BDL_DQ3_BIT);
|
|
dq47 = ((bdl[4] - min) << PHY_BDL_DQ0_BIT) | ((bdl[5] - min) << PHY_BDL_DQ1_BIT) |
|
|
((bdl[6] - min) << PHY_BDL_DQ2_BIT) | ((bdl[7] - min) << PHY_BDL_DQ3_BIT);
|
|
rdm = (rdm & (~(PHY_RDM_BDL_MASK << PHY_RDM_BDL_BIT))) | ((bdl[8] - min) << PHY_RDM_BDL_BIT);
|
|
rdqs = (rdqs & (~(PHY_RDQS_BDL_MASK << PHY_RDQS_BDL_BIT))) | ((bdl[9] - min) << PHY_RDQS_BDL_BIT);
|
|
|
|
ddr_write(dq03, base_phy + DDR_PHY_DXNRDQNBDL0(rank, byte_idx));
|
|
ddr_write(dq47, base_phy + DDR_PHY_DXNRDQNBDL1(rank, byte_idx));
|
|
ddr_write(rdm, base_phy + DDR_PHY_DXNRDQNBDL2(rank, byte_idx));
|
|
ddr_write(rdqs, base_phy + DDR_PHY_DXNRDQSDLY(byte_idx));
|
|
}
|
|
|
|
#define __ddrt__
|
|
#ifdef DDR_DDRT_SPECIAL_CONFIG
|
|
/* Some special DDRT need read register repeatedly */
|
|
static unsigned int ddr_ddrt_read(unsigned int addr)
|
|
{
|
|
int times = 0;
|
|
unsigned int data0, data1, data2;
|
|
do {
|
|
data0 = ddr_read(addr);
|
|
data1 = ddr_read(addr);
|
|
data2 = ddr_read(addr);
|
|
times++;
|
|
} while (((data0 != data1) || (data1 != data2))
|
|
&& (times < DDRT_READ_TIMEOUT));
|
|
|
|
if (times >= DDRT_READ_TIMEOUT) {
|
|
DDR_FATAL("DDRT wait timeout.");
|
|
ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, 0, -1, -1);
|
|
}
|
|
|
|
return data0;
|
|
}
|
|
|
|
/* Some special DDRT need write twice register */
|
|
static void ddr_ddrt_write(unsigned int data, unsigned int addr)
|
|
{
|
|
unsigned int tmp;
|
|
tmp = ddr_read(addr);
|
|
ddr_write(data, addr);
|
|
ddr_write(data, addr);
|
|
}
|
|
#endif /* DDR_DDRT_SPECIAL_CONFIG */
|
|
|
|
static unsigned int ddr_get_rank_size(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int base_dmc = cfg->cur_dmc;
|
|
unsigned int rnkvol;
|
|
unsigned int mem_bank, mem_row, mem_col, mem_width;
|
|
unsigned int size;
|
|
|
|
mem_width = (ddr_read(base_dmc + DDR_DMC_CFG_DDRMODE) >> DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK;
|
|
rnkvol = ddr_read(base_dmc + DDR_DMC_CFG_RNKVOL(0));
|
|
mem_bank = (rnkvol >> DMC_RNKVOL_MEM_BANK_BIT) & DMC_RNKVOL_MEM_BANK_MASK;
|
|
mem_row = (rnkvol >> DMC_RNKVOL_MEM_ROW_BIT) & DMC_RNKVOL_MEM_ROW_MASK;
|
|
mem_col = rnkvol & DMC_RNKVOL_MEM_COL_MASK;
|
|
|
|
size = 1UL << ((mem_bank + 2) + (mem_row + 11) + (mem_col + 8) + mem_width);
|
|
DDR_DEBUG("rank size[%x]", size);
|
|
|
|
return size;
|
|
}
|
|
|
|
/* Init DDRT register before DDRT test */
|
|
void ddr_ddrt_init(struct ddr_cfg_st *cfg, unsigned int mode)
|
|
{
|
|
unsigned int mem_width;
|
|
unsigned int mem_config;
|
|
unsigned int offset = 0;
|
|
|
|
if (1 == cfg->rank_idx)
|
|
offset = ddr_get_rank_size(cfg);
|
|
|
|
DDR_TRAINING_DDRT_PREPARE_FUNC();
|
|
|
|
mem_width = ((ddr_read(cfg->cur_dmc + DDR_DMC_CFG_DDRMODE)
|
|
>> DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK);
|
|
mem_config = ((mem_width - 1) << DDRT_DDR_MEM_WIDTH)
|
|
| DDRT_DDR_COL_WIDTH | DDRT_DDR_ROW_WIDTH
|
|
| DDRT_DDR_BANK_WIDTH;
|
|
/* DDRT SDRAM config */
|
|
DDRT_REG_WRITE(mem_config, DDR_REG_BASE_DDRT + DDRT_MEM_CONFIG);
|
|
/* DDR Address Base */
|
|
DDRT_REG_WRITE(DDRT_GET_TEST_ADDR(DDRT_CFG_BASE_ADDR),
|
|
DDR_REG_BASE_DDRT + DDRT_DDR_BASE_ADDR);
|
|
/* DDRT test DDR using space */
|
|
DDRT_REG_WRITE(DDRT_GET_TEST_ADDR(ddr_ddrt_get_test_addr() + offset),
|
|
DDR_REG_BASE_DDRT + DDRT_ADDR);
|
|
DDRT_REG_WRITE(DDRT_CFG_SEED, DDR_REG_BASE_DDRT + DDRT_SEED);
|
|
|
|
if (DDR_DDRT_MODE_GATE == mode) {
|
|
/* Read or Write Once */
|
|
DDRT_REG_WRITE(DDRT_CFG_BURST_CFG_GATE,
|
|
DDR_REG_BASE_DDRT + DDRT_BURST_CONFIG);
|
|
DDRT_REG_WRITE(0x0, DDR_REG_BASE_DDRT + DDRT_BURST_NUM);
|
|
DDRT_REG_WRITE(0x0, DDR_REG_BASE_DDRT + DDRT_ADDR_NUM);
|
|
DDRT_REG_WRITE(0x0, DDR_REG_BASE_DDRT + DDRT_LOOP_NUM);
|
|
DDRT_REG_WRITE(DDRT_CFG_REVERSED,
|
|
DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ);
|
|
} else {
|
|
/* reversed data form register init table */
|
|
/* 128bit BURST4 */
|
|
DDRT_REG_WRITE(DDRT_CFG_BURST_CFG_DATAEYE,
|
|
DDR_REG_BASE_DDRT + DDRT_BURST_CONFIG);
|
|
DDRT_REG_WRITE(cfg->phy[cfg->phy_idx].dmc[cfg->dmc_idx].ddrt_pattern,
|
|
DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ);
|
|
DDRT_REG_WRITE(DDRT_CFG_BURST_NUM,
|
|
DDR_REG_BASE_DDRT + DDRT_BURST_NUM);
|
|
DDRT_REG_WRITE(DDRT_CFG_ADDR_NUM,
|
|
DDR_REG_BASE_DDRT + DDRT_ADDR_NUM);
|
|
DDRT_REG_WRITE(DDRT_CFG_LOOP_NUM,
|
|
DDR_REG_BASE_DDRT + DDRT_LOOP_NUM);
|
|
}
|
|
|
|
DDR_DEBUG("DDRT ADDR[%x = %x]", (DDR_REG_BASE_DDRT + DDRT_ADDR),
|
|
ddr_read(DDR_REG_BASE_DDRT + DDRT_ADDR));
|
|
}
|
|
|
|
/**
|
|
* ddr_ddrt_test
|
|
* @mask : DDRT option mask.
|
|
* @byte : DDR byte index.
|
|
* @dq : DDR dq index.
|
|
*
|
|
* DDRT test. Support read_only mode and write_read_compare mode.
|
|
* Success return 0, fail return -1.
|
|
*/
|
|
int ddr_ddrt_test(unsigned int mask, int byte, int dq)
|
|
{
|
|
unsigned int regval;
|
|
unsigned int err_ovfl;
|
|
unsigned int err_cnt;
|
|
unsigned int dq_num;
|
|
unsigned int dq_tmp;
|
|
unsigned int times = 0;
|
|
|
|
DDRT_REG_WRITE(mask | DDRT_CFG_START, DDR_REG_BASE_DDRT + DDRT_OP);
|
|
DDRT_REG_WRITE(0, DDR_REG_BASE_DDRT + DDRT_STATUS);
|
|
|
|
DDR_ASM_DSB();
|
|
|
|
do {
|
|
regval = DDRT_REG_READ(DDR_REG_BASE_DDRT + DDRT_STATUS);
|
|
times++;
|
|
} while ((!(regval & DDRT_TEST_DONE_MASK))
|
|
&& (times < DDRT_WAIT_TIMEOUT));
|
|
|
|
if (times >= DDRT_WAIT_TIMEOUT) {
|
|
DDR_FATAL("DDRT wait timeout.");
|
|
ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, 0, -1, -1);
|
|
return -1;
|
|
}
|
|
|
|
/* DDRT_READ_ONLY_MODE */
|
|
if (DDRT_READ_ONLY_MODE == (mask & DDRT_TEST_MODE_MASK))
|
|
return 0; /* return when DDRT finish */
|
|
|
|
/* DDRT_WR_COMPRARE_MODE No error occurred, test pass. */
|
|
if (regval & DDRT_TEST_PASS_MASK)
|
|
return 0;
|
|
|
|
if (-1 != dq) { /* check for dq */
|
|
dq_num = ((unsigned int)byte << 3) + dq;
|
|
err_ovfl = DDRT_REG_READ(DDR_REG_BASE_DDRT
|
|
+ DDRT_DQ_ERR_OVFL) & (1 << dq_num);
|
|
if (err_ovfl)
|
|
return -1;
|
|
|
|
if (dq > 3)
|
|
dq_tmp = (unsigned int)(dq - 4) << 3;
|
|
else
|
|
dq_tmp = (unsigned int)dq << 3;
|
|
err_cnt = DDRT_REG_READ(DDR_REG_BASE_DDRT
|
|
+ DDRT_DQ_ERR_CNT(((unsigned int)byte << 1) + ((unsigned int)dq >> 2)));
|
|
err_cnt = err_cnt & (0xff << dq_tmp);
|
|
if (err_cnt)
|
|
return -1;
|
|
} else if (-1 != byte) { /* check for byte */
|
|
err_ovfl = DDRT_REG_READ(DDR_REG_BASE_DDRT
|
|
+ DDRT_DQ_ERR_OVFL) & (0xff << ((unsigned int)byte << 3));
|
|
if (err_ovfl)
|
|
return -1;
|
|
|
|
err_cnt = DDRT_REG_READ(DDR_REG_BASE_DDRT
|
|
+ DDRT_DQ_ERR_CNT((unsigned int)byte << 1));
|
|
err_cnt += DDRT_REG_READ(DDR_REG_BASE_DDRT
|
|
+ DDRT_DQ_ERR_CNT(((unsigned int)byte << 1) + 1));
|
|
if (err_cnt)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Check ddrt test result. Success return 0, fail return -1 */
|
|
static int ddr_ddrt_check(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int byte_index_to_dmc = cfg->cur_byte;
|
|
|
|
/* ddrt test the byte relate to dmc, make sure not overflow */
|
|
if (cfg->cur_byte >= (cfg->dmc_idx << 1))
|
|
byte_index_to_dmc = cfg->cur_byte - (cfg->dmc_idx << 1);
|
|
|
|
DDRT_REG_WRITE(0, DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ);
|
|
if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE | DDRT_PATTERM_PRBS9,
|
|
byte_index_to_dmc, cfg->cur_dq))
|
|
return -1;
|
|
|
|
DDRT_REG_WRITE(cfg->cur_pattern, DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ);
|
|
if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE | DDRT_PATTERM_PRBS11,
|
|
byte_index_to_dmc, cfg->cur_dq))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define __dataeye_adjust__
|
|
#ifdef DDR_TRAINING_ADJUST_CONFIG
|
|
static unsigned int ddr_adjust_get_average(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int dq0_3, dq4_7, val;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_index = cfg->cur_byte;
|
|
unsigned int rank = cfg->rank_idx;
|
|
|
|
if (DDR_MODE_WRITE == cfg->cur_mode)
|
|
return (ddr_read(base_phy + DDR_PHY_DXNWDQNBDL2(rank, byte_index))
|
|
>> PHY_WDM_BDL_BIT) & PHY_BDL_MASK;
|
|
|
|
/* read */
|
|
dq0_3 = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL0(rank, byte_index));
|
|
dq4_7 = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL1(rank, byte_index));
|
|
|
|
val = ((dq0_3 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK)
|
|
+ ((dq0_3 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK)
|
|
+ ((dq0_3 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK)
|
|
+ ((dq0_3 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK)
|
|
+ ((dq4_7 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK)
|
|
+ ((dq4_7 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK)
|
|
+ ((dq4_7 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK)
|
|
+ ((dq4_7 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK);
|
|
|
|
val = val >> 3;
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* ddr_adjust_trend_check
|
|
* @accel : Return a value to adjust quickly.
|
|
*
|
|
* Check dataeye DQ window on left or right or middle.
|
|
*/
|
|
static unsigned int ddr_adjust_trend_check(struct ddr_cfg_st *cfg, int *accel)
|
|
{
|
|
unsigned int dq_bdl = 0;
|
|
unsigned int size;
|
|
|
|
/* 32 BDL middle[13, 17]. 128 BDL middle[40, 56] */
|
|
/* 1 Phase = (DDR_BDL_PHASE_TRANSFORM) BDL */
|
|
size = DDR_BDL_PHASE_TRANSFORM >> 1;
|
|
|
|
dq_bdl = ddr_adjust_get_average(cfg);
|
|
|
|
/* increase adjust step to accelerate */
|
|
if (accel) {
|
|
if (dq_bdl > PHY_DQ_BDL_MIDDLE)
|
|
*accel = dq_bdl - PHY_DQ_BDL_MIDDLE;
|
|
else if (dq_bdl < PHY_DQ_BDL_MIDDLE)
|
|
*accel = PHY_DQ_BDL_MIDDLE - dq_bdl;
|
|
|
|
DDR_INFO("byte[%x] bdl[%x] middle[%x] accel[%x] rdqs[%x]",
|
|
cfg->cur_byte, dq_bdl, PHY_DQ_BDL_MIDDLE, *accel,
|
|
(ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQSDLY(cfg->cur_byte))
|
|
>> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK);
|
|
}
|
|
|
|
/* window on left */
|
|
if (dq_bdl < (PHY_DQ_BDL_MIDDLE - size))
|
|
return DDR_WIN_LEFT;
|
|
/* on right */
|
|
else if (dq_bdl > (PHY_DQ_BDL_MIDDLE + size))
|
|
return DDR_WIN_RIGHT;
|
|
else
|
|
return DDR_WIN_MIDDLE;
|
|
}
|
|
|
|
/* Check adjust value whether valid */
|
|
static int ddr_adjust_check_val(int val, unsigned int mode)
|
|
{
|
|
if (DDR_MODE_READ == mode) {
|
|
if (val < 0 || val > PHY_RDQS_BDL_MASK)
|
|
return DDR_FALSE;
|
|
} else {
|
|
if (val < 0 || val > PHY_WDQ_PHASE_MASK)
|
|
return DDR_FALSE;
|
|
}
|
|
|
|
return DDR_TRUE;
|
|
}
|
|
|
|
/* Get value which need to adjust */
|
|
static int ddr_adjust_get_val(struct ddr_cfg_st *cfg)
|
|
{
|
|
if (DDR_MODE_READ == cfg->cur_mode)
|
|
return (ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQSDLY(cfg->cur_byte))
|
|
>> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK;
|
|
else
|
|
return (ddr_read(cfg->cur_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, cfg->cur_byte))
|
|
>> PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK;
|
|
}
|
|
|
|
static void ddr_rdqs_sync(struct ddr_cfg_st *cfg, int val)
|
|
{
|
|
unsigned int rdqsdly;
|
|
unsigned int cur_rank = cfg->rank_idx;
|
|
int old, offset;
|
|
|
|
rdqsdly = ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQSDLY(cfg->cur_byte));
|
|
old = (rdqsdly >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK;
|
|
offset = val - old;
|
|
|
|
/* sync rdm */
|
|
ddr_rdqs_sync_rank_rdq(cfg, offset);
|
|
|
|
if (1 == cfg->phy[cfg->phy_idx].rank_num) {
|
|
DDR_DEBUG("Rank number[%x] not need sync another rank", cfg->phy[cfg->phy_idx].rank_num);
|
|
return;
|
|
}
|
|
|
|
/* sync other rank rdm and rdq */
|
|
cfg->rank_idx = DDR_SUPPORT_RANK_MAX - 1 - cur_rank; /* switch to another rank */
|
|
ddr_rdqs_sync_rank_rdq(cfg, offset);
|
|
cfg->rank_idx = cur_rank; /* resotre to cur rank */
|
|
}
|
|
|
|
static void ddr_set_rdqs(struct ddr_cfg_st *cfg, int val)
|
|
{
|
|
unsigned int delay;
|
|
delay = ddr_read(cfg->cur_phy + DDR_PHY_DXNRDQSDLY(cfg->cur_byte));
|
|
|
|
DDR_PHY_RDQS_SYNC_RDM(cfg, val);
|
|
|
|
/* clear rdqs bdl */
|
|
delay = delay & (~(PHY_RDQS_BDL_MASK << PHY_RDQS_BDL_BIT));
|
|
|
|
ddr_write(delay | ((unsigned int)val << PHY_RDQS_BDL_BIT),
|
|
cfg->cur_phy + DDR_PHY_DXNRDQSDLY(cfg->cur_byte));
|
|
}
|
|
|
|
/* Set value which need to adjust */
|
|
static void ddr_adjust_set_val(struct ddr_cfg_st *cfg, int val)
|
|
{
|
|
unsigned int delay;
|
|
if (DDR_MODE_READ == cfg->cur_mode) {
|
|
ddr_set_rdqs(cfg, val);
|
|
} else {
|
|
delay = ddr_read(cfg->cur_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, cfg->cur_byte));
|
|
/* clear wdq phase */
|
|
delay = delay & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT));
|
|
|
|
ddr_write(delay | ((unsigned int)val << PHY_WDQ_PHASE_BIT),
|
|
cfg->cur_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, cfg->cur_byte));
|
|
}
|
|
|
|
ddr_phy_cfg_update(cfg->cur_phy);
|
|
}
|
|
|
|
/* Add or delete value to adjust */
|
|
static void ddr_adjust_change_val(unsigned int dir, int *val,
|
|
int step, unsigned int mode)
|
|
{
|
|
if (DDR_MODE_READ == mode) {
|
|
if (DDR_WIN_RIGHT == dir)
|
|
(*val) = (*val) + step;
|
|
else
|
|
(*val) = (*val) - step;
|
|
} else {
|
|
/* decrease wdq phase, window move to right */
|
|
if (DDR_WIN_RIGHT == dir)
|
|
(*val) = (*val) - step;
|
|
else
|
|
(*val) = (*val) + step;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ddr_adjust_move_win
|
|
* @dir : move direction. DDR_TRUE move to right, DDR_FALSE move to left.
|
|
*
|
|
* Move window to specified direction until the best DQ bdl beyond the midline.
|
|
*/
|
|
static void ddr_adjust_move_win(struct ddr_cfg_st *cfg,
|
|
struct training_data *training,
|
|
int step, unsigned int dir)
|
|
{
|
|
int cur_val, def_val;
|
|
int i;
|
|
int accel;
|
|
int trend;
|
|
unsigned int max_value;
|
|
|
|
max_value = (DDR_MODE_WRITE == cfg->cur_mode ?
|
|
PHY_WDQ_PHASE_MASK : PHY_RDQS_BDL_MASK);
|
|
|
|
def_val = ddr_adjust_get_val(cfg);
|
|
cur_val = def_val;
|
|
for (i = 0; i <= max_value; i++) {
|
|
accel = step;
|
|
/* write mode no need to accelerate */
|
|
if (DDR_MODE_WRITE == cfg->cur_mode)
|
|
trend = ddr_adjust_trend_check(cfg, 0);
|
|
else
|
|
trend = ddr_adjust_trend_check(cfg, &accel);
|
|
|
|
if (DDR_WIN_MIDDLE == trend || dir == trend) {
|
|
DDR_DEBUG("Move byte[%x] window to middle suc", cfg->cur_byte);
|
|
break;
|
|
}
|
|
|
|
ddr_adjust_change_val(dir, &cur_val, accel, cfg->cur_mode);
|
|
if (DDR_FALSE == ddr_adjust_check_val(cur_val, cfg->cur_mode)) {
|
|
DDR_WARNING("Move byte[%x] to middle fail. value[%x]",
|
|
cfg->cur_byte, cur_val);
|
|
break;
|
|
}
|
|
|
|
DDR_DEBUG("Byte[%x] mode[%x] set value[%x]",
|
|
cfg->cur_byte, cfg->cur_mode, cur_val);
|
|
ddr_adjust_set_val(cfg, cur_val);
|
|
if (ddr_dataeye_deskew(cfg, training)) {
|
|
ddr_adjust_set_val(cfg, def_val);
|
|
/* MUST deskew dataeye after restore rdqs */
|
|
ddr_dataeye_deskew(cfg, training);
|
|
DDR_ERROR("Byte[%x] deskew fail, restore[%x]",
|
|
cfg->cur_byte, def_val);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Adjust specified byte winodw to middle */
|
|
static void ddr_adjust_byte(struct ddr_cfg_st *cfg, struct training_data *training)
|
|
{
|
|
unsigned int trend = ddr_adjust_trend_check(cfg, 0);
|
|
|
|
/* window on left, move to right */
|
|
if (DDR_WIN_LEFT == trend)
|
|
ddr_adjust_move_win(cfg, training, DDR_DQS_ADJ_STEP, DDR_WIN_RIGHT);
|
|
/* window on right, move to left */
|
|
else if (DDR_WIN_RIGHT == trend)
|
|
ddr_adjust_move_win(cfg, training, DDR_DQS_ADJ_STEP, DDR_WIN_LEFT);
|
|
/* window on middle, no need to move */
|
|
else
|
|
DDR_DEBUG("Byte[%x] mode[%x] win on middle.",
|
|
cfg->cur_byte, cfg->cur_mode);
|
|
}
|
|
|
|
/**
|
|
* Adjust PHY dataeye. On normal case,
|
|
* read dateeye window on left after read dataeye hardware training,
|
|
* write dataeye window on left after write leveling training.
|
|
*
|
|
*/
|
|
void ddr_adjust_dataeye(struct ddr_cfg_st *cfg, struct training_data *training)
|
|
{
|
|
int i;
|
|
|
|
/* dataeye adjust disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_ADJ_MASK))
|
|
return;
|
|
|
|
DDR_DEBUG("DDR dataeye adjust PHY[%x][%x] DMC[%x][%x] Rank[%x]",
|
|
cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx);
|
|
|
|
if (DDR_FALSE == cfg->adjust)
|
|
return;
|
|
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
ddr_adjust_byte(cfg, training);
|
|
}
|
|
}
|
|
#else
|
|
#define ddr_adjust_dataeye(cfg, training)
|
|
#endif /* DDR_TRAINING_ADJUST_CONFIG */
|
|
|
|
#define __dataeye_training__
|
|
#ifdef DDR_DATAEYE_TRAINING_CONFIG
|
|
/* Check dataeye dq */
|
|
int ddr_dataeye_check_dq(struct ddr_cfg_st *cfg)
|
|
{
|
|
if (DDR_CHECK_TYPE_DDRT == cfg->dq_check_type)
|
|
return ddr_ddrt_check(cfg);
|
|
else if (DDR_CHECK_TYPE_MPR == cfg->dq_check_type)
|
|
return ddr_mpr_check(cfg);
|
|
else
|
|
DDR_ERROR("DDR dataeye dq check type not set.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Check dq whether valid and set mask to reduce search time */
|
|
static int ddr_dataeye_check_dir(unsigned int direction, unsigned int left,
|
|
unsigned int right, unsigned int *mask,
|
|
struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
|
|
result = ddr_dataeye_check_dq(cfg);
|
|
switch (direction) {
|
|
case DDR_FIND_DQ_BOTH:
|
|
*mask = DDR_FIND_DQ_LEFT | DDR_FIND_DQ_RIGHT;
|
|
break;
|
|
case DDR_FIND_DQ_LEFT:
|
|
if (result) {
|
|
/* ddr test error, search opposite side */
|
|
*mask = DDR_FIND_DQ_RIGHT;
|
|
} else { /* ddr test ok */
|
|
ddr_phy_set_dq_bdl(cfg, left);
|
|
if (!ddr_dataeye_check_dq(cfg))
|
|
/* test ok, go on search this side */
|
|
*mask = DDR_FIND_DQ_LEFT;
|
|
}
|
|
break;
|
|
case DDR_FIND_DQ_RIGHT:
|
|
if (result) { /* ddr test error, search opposite side */
|
|
*mask = DDR_FIND_DQ_LEFT;
|
|
} else { /* ddr test ok */
|
|
ddr_phy_set_dq_bdl(cfg, right);
|
|
if (!ddr_dataeye_check_dq(cfg))
|
|
/* test OK, go on search this side */
|
|
*mask = DDR_FIND_DQ_RIGHT;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Binary search the valid dq bdl */
|
|
static void ddr_dataeye_search_dq(unsigned int left, unsigned int right,
|
|
int *target, unsigned int direction,
|
|
struct ddr_cfg_st *cfg)
|
|
|
|
{
|
|
unsigned int middle;
|
|
unsigned int mask = 0;
|
|
|
|
middle = left + ((right - left) >> 1);
|
|
|
|
ddr_phy_set_dq_bdl(cfg, middle);
|
|
if (!ddr_dataeye_check_dir(direction, left, right, &mask, cfg)) { /* test ok */
|
|
*target = (int)middle;
|
|
return;
|
|
}
|
|
|
|
if (left == middle || middle == right) /* not found */
|
|
return;
|
|
|
|
/* find left side */
|
|
if (DDR_FIND_DQ_LEFT & mask)
|
|
ddr_dataeye_search_dq(left, middle, target, direction, cfg);
|
|
|
|
/* find right side */
|
|
if (DDR_FIND_DQ_RIGHT & mask)
|
|
ddr_dataeye_search_dq(middle, right, target, direction, cfg);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Find DQ valid range */
|
|
static void ddr_dataeye_find_dq(struct ddr_cfg_st *cfg,
|
|
struct training_data *training)
|
|
{
|
|
int cur_dq, left_dq, right_dq, def_dq;
|
|
unsigned int dq_num;
|
|
unsigned int win_num;
|
|
|
|
dq_num = (cfg->cur_byte << 3) + cfg->cur_dq;
|
|
def_dq = (int)ddr_phy_get_dq_bdl(cfg);
|
|
cur_dq = def_dq;
|
|
|
|
/* check default dq */
|
|
if (ddr_dataeye_check_dq(cfg)) {
|
|
/* test error */
|
|
cur_dq = -1;
|
|
ddr_dataeye_search_dq(0, PHY_BDL_MASK, &cur_dq,
|
|
DDR_FIND_DQ_BOTH, cfg);
|
|
DDR_DEBUG("DQ[%x] def[%x] nok, find new value[%x]",
|
|
dq_num, def_dq, cur_dq);
|
|
if (-1 == cur_dq) { /* no valid dq */
|
|
training->ddr_bit_result[dq_num] = 0;
|
|
training->ddr_bit_best[dq_num] = 0;
|
|
/* restore default value */
|
|
ddr_phy_set_dq_bdl(cfg, def_dq);
|
|
DDR_WARNING("DQ[%x] not found dq. restore[%x]", dq_num, def_dq);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* find the left boundary */
|
|
left_dq = cur_dq;
|
|
ddr_dataeye_search_dq(0, cur_dq, &left_dq,
|
|
DDR_FIND_DQ_LEFT, cfg);
|
|
while (left_dq > 0) {
|
|
left_dq--;
|
|
ddr_phy_set_dq_bdl(cfg, left_dq);
|
|
if (ddr_dataeye_check_dq(cfg)) {
|
|
/* test error */
|
|
left_dq++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* find the right boundary */
|
|
right_dq = cur_dq;
|
|
ddr_dataeye_search_dq(cur_dq, PHY_BDL_MASK, &right_dq,
|
|
DDR_FIND_DQ_RIGHT, cfg);
|
|
while (right_dq < PHY_BDL_MASK) {
|
|
right_dq++;
|
|
ddr_phy_set_dq_bdl(cfg, right_dq);
|
|
if (ddr_dataeye_check_dq(cfg)) {
|
|
/* test error */
|
|
right_dq--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* reset dq */
|
|
ddr_phy_set_dq_bdl(cfg, def_dq);
|
|
|
|
/**
|
|
* 0 1 2 3 4 5 6 7 8 9
|
|
* x x - - - - - x x x
|
|
* | |
|
|
* left_dq right_dq
|
|
*
|
|
* so left_dq = 2, right_dq = 6
|
|
*/
|
|
/* set result */
|
|
win_num = right_dq - left_dq + 1;
|
|
training->ddr_bit_result[dq_num] = ((unsigned int)left_dq << DDR_DATAEYE_RESULT_BIT
|
|
| (unsigned int)right_dq);
|
|
training->ddr_bit_best[dq_num] = (win_num << DDR_DATAEYE_RESULT_BIT)
|
|
| ((win_num >> 1) + (unsigned int)left_dq);
|
|
|
|
DDR_INFO("DQ[%x] range: left[%x] right[%x] best[%x] mode[%x] rank[%x]", dq_num,
|
|
left_dq, right_dq, training->ddr_bit_best[dq_num], cfg->cur_mode, cfg->rank_idx);
|
|
}
|
|
|
|
/* DDR dataeye training one byte */
|
|
int ddr_dataeye_deskew(struct ddr_cfg_st *cfg, struct training_data *training)
|
|
{
|
|
unsigned int dq_num;
|
|
unsigned int loop_times = 0;
|
|
unsigned int win_num, dq_sum;
|
|
unsigned int def_dq, best_dq;
|
|
int i;
|
|
unsigned int byte_index = cfg->cur_byte;
|
|
|
|
dq_sum = 0;
|
|
training->ddr_win_sum = 0;
|
|
for (i = 0; i < DDR_PHY_BIT_NUM; i++) {
|
|
cfg->cur_dq = i;
|
|
dq_num = (byte_index << 3) + i;
|
|
def_dq = ddr_phy_get_dq_bdl(cfg);
|
|
ddr_dataeye_find_dq(cfg, training);
|
|
win_num = training->ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT;
|
|
best_dq = training->ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK;
|
|
/* check window number */
|
|
if (win_num < DDR_DATAEYE_WIN_NUM) {
|
|
if (loop_times < DDR_LOOP_TIMES_LMT) {
|
|
loop_times++;
|
|
i--;
|
|
continue;
|
|
} else {
|
|
if (win_num == 0) {
|
|
DDR_WARNING("Byte[%x] DQ[%x] no win.", byte_index, dq_num);
|
|
/* restore default value */
|
|
ddr_phy_set_dq_bdl(cfg, def_dq);
|
|
ddr_training_stat(DDR_ERR_DATAEYE, cfg->cur_phy, byte_index, i);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
loop_times = 0;
|
|
ddr_phy_set_dq_bdl(cfg, best_dq);
|
|
dq_sum = dq_sum + best_dq;
|
|
training->ddr_win_sum = training->ddr_win_sum + win_num;
|
|
}
|
|
|
|
dq_sum = dq_sum >> 3;
|
|
|
|
/* only DDR_MODE_WRITE need to set */
|
|
if (DDR_MODE_WRITE == cfg->cur_mode)
|
|
ddr_write((dq_sum & PHY_BDL_MASK) << PHY_WDM_BDL_BIT, cfg->cur_phy
|
|
+ DDR_PHY_DXNWDQNBDL2(cfg->rank_idx, byte_index));
|
|
|
|
ddr_phy_cfg_update(cfg->cur_phy);
|
|
return 0;
|
|
}
|
|
|
|
/* DDR write or read dataeye training */
|
|
static int ddr_dataeye_process(struct ddr_cfg_st *cfg,
|
|
struct training_data *training)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
|
|
/* dataeye training */
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
result += ddr_dataeye_deskew(cfg, training);
|
|
}
|
|
|
|
if (result) {
|
|
result = -1;
|
|
DDR_ERROR("PHY[%x] mode[%x] dataeye training fail", cfg->cur_phy, cfg->cur_mode);
|
|
} else {
|
|
/* dataeye training result adjust */
|
|
ddr_adjust_dataeye(cfg, training);
|
|
}
|
|
|
|
/* save training result to printf */
|
|
ddr_result_data_save(cfg, training);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* DDR dataeye training */
|
|
int ddr_dataeye_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct training_data tmp_result;
|
|
struct training_data *training = &tmp_result;
|
|
int result_read, result_write;
|
|
|
|
DDR_DEBUG("DDR dataeye training PHY[%x][%x] DMC[%x][%x] Rank[%x]",
|
|
cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx);
|
|
|
|
/* write dataeye training */
|
|
cfg->cur_mode = DDR_MODE_WRITE;
|
|
ddrtr_memset(training, 0, sizeof(struct training_data));
|
|
result_write = ddr_dataeye_process(cfg, training);
|
|
|
|
/* read dataeye training */
|
|
cfg->cur_mode = DDR_MODE_READ;
|
|
ddrtr_memset(training, 0, sizeof(struct training_data));
|
|
result_read = ddr_dataeye_process(cfg, training);
|
|
|
|
if (result_read || result_write)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int ddr_dataeye_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct tr_relate_reg relate_reg;
|
|
int result;
|
|
|
|
/* dataeye training disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_MASK))
|
|
return 0;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_DATAEYE_MASK);
|
|
ddr_training_switch_axi(cfg);
|
|
ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE);
|
|
cfg->adjust = DDR_DATAEYE_NORMAL_ADJUST;
|
|
cfg->dq_check_type = DDR_CHECK_TYPE_DDRT;
|
|
result = ddr_dataeye_training(cfg);
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_dataeye_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DDR dataeye training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_DATAEYE_TRAINING_CONFIG */
|
|
|
|
#define __hardware_training__
|
|
#ifdef DDR_HW_TRAINING_CONFIG
|
|
#ifdef DDR_HW_READ_ADJ_CONFIG
|
|
/**
|
|
* Adjust rdqs and dq after hw read training.
|
|
* When define DDR_TRAINING_ADJUST_DISABLE, MUST define DDR_HW_READ_ADJ_CONFIG.
|
|
*/
|
|
static void ddr_hw_read_adj(struct ddr_cfg_st *cfg)
|
|
{
|
|
int i;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
|
|
DDR_DEBUG("DDR hw read adjust.");
|
|
/* check hw read adjust bypass bit */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_HW_ADJ_MASK))
|
|
return;
|
|
|
|
/* assume read dataeye window on left */
|
|
for (i = 0; i < byte_num; i++) {
|
|
ddr_write(ddr_read(base_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, i))
|
|
+ (PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT),
|
|
base_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, i));
|
|
ddr_write(ddr_read(base_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, i))
|
|
+ (PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT),
|
|
base_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, i));
|
|
ddr_write(ddr_read(base_phy + DDR_PHY_DXNRDQSDLY(i))
|
|
+ (PHY_RDQS_MIDDLE_VAL << PHY_RDQS_BDL_BIT),
|
|
base_phy + DDR_PHY_DXNRDQSDLY(i));
|
|
}
|
|
}
|
|
#else
|
|
static void ddr_hw_read_adj(struct ddr_cfg_st *cfg) {}
|
|
#endif /* DDR_HW_READ_ADJ_CONFIG */
|
|
|
|
static void ddr_training_get_rdqs(struct ddr_cfg_st *cfg, struct ddr_bdl_st *rdqs)
|
|
{
|
|
unsigned int i;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
for(i = 0; i < byte_num; i++) {
|
|
rdqs->bdl[i] = ddr_read(base_phy + DDR_PHY_DXNRDQSDLY(i));
|
|
}
|
|
}
|
|
|
|
static void ddr_training_set_rdqs(struct ddr_cfg_st *cfg, struct ddr_bdl_st *rdqs)
|
|
{
|
|
unsigned int i;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
for (i = 0; i < byte_num; i++) {
|
|
ddr_write(rdqs->bdl[i], base_phy + DDR_PHY_DXNRDQSDLY(i));
|
|
}
|
|
}
|
|
|
|
static void ddr_hw_training_adjust_rdqs(struct ddr_cfg_st *cfg, struct rdqs_data_st *rdqs_st)
|
|
{
|
|
unsigned int i;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
unsigned int rdqs_rank0, rdqs_rank1;
|
|
unsigned int cur_rank = cfg->rank_idx;
|
|
int offset;
|
|
|
|
for (i = 0; i < byte_num; i++) {
|
|
/* struct rdqs_data_st store the whole register value */
|
|
rdqs_rank0 = (rdqs_st->rank[0].bdl[i] >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK;
|
|
rdqs_rank1 = (rdqs_st->rank[1].bdl[i] >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK;
|
|
|
|
cfg->cur_byte = i;
|
|
if (rdqs_rank0 > rdqs_rank1) {
|
|
offset = rdqs_rank0 - rdqs_rank1;
|
|
ddr_write(rdqs_st->rank[0].bdl[i], cfg->cur_phy+ DDR_PHY_DXNRDQSDLY(i));
|
|
cfg->rank_idx = 1; /* switch to rank1 for sync rank1 rdq */
|
|
} else {
|
|
offset = rdqs_rank1 - rdqs_rank0;
|
|
ddr_write(rdqs_st->rank[1].bdl[i], cfg->cur_phy+ DDR_PHY_DXNRDQSDLY(i));
|
|
cfg->rank_idx = 0; /* switch to rank0 for sync rank0 rdq */
|
|
}
|
|
ddr_rdqs_sync_rank_rdq(cfg, offset);
|
|
}
|
|
|
|
cfg->rank_idx = cur_rank; /* restore to current rank */
|
|
|
|
ddr_phy_cfg_update(cfg->cur_phy);
|
|
}
|
|
|
|
/* DDR HW training process */
|
|
static int ddr_hw_training_process(struct ddr_cfg_st *cfg, unsigned int item)
|
|
{
|
|
unsigned int count = DDR_HWR_WAIT_TIMEOUT;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int init_ctrl = ddr_read(base_phy + DDR_PHY_PHYINITCTRL);
|
|
|
|
if (!item)
|
|
return 0;
|
|
|
|
DDR_DEBUG("base_phy[%x] itme[%x]", base_phy, item);
|
|
/* hardware training enable */
|
|
ddr_write(item | PHY_PHYINITCTRL_INIT_EN | init_ctrl, base_phy + DDR_PHY_PHYINITCTRL);
|
|
|
|
if (item & PHY_PHYINITCTRL_DRAM_RST) {
|
|
if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF))
|
|
return -1;
|
|
}
|
|
|
|
count = DDR_HWR_WAIT_TIMEOUT;
|
|
/* auto cleared to 0 after training finished */
|
|
while (count--) {
|
|
if (!(ddr_read(base_phy + DDR_PHY_PHYINITCTRL)
|
|
& PHY_PHYINITCTRL_MASK))
|
|
break;
|
|
}
|
|
|
|
if (count == 0xffffffff) {
|
|
DDR_FATAL("HWR wait timeout.");
|
|
ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, item, ddr_read(base_phy + DDR_PHY_PHYINITSTATUS)); /* TODO: */
|
|
return -1;
|
|
}
|
|
|
|
if (ddr_read(base_phy + DDR_PHY_PHYINITSTATUS)) {
|
|
DDR_FATAL("Phy[%x] hw[%x] failed[%x]", base_phy, item, ddr_read(base_phy + DDR_PHY_PHYINITSTATUS));
|
|
ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, item, ddr_read(base_phy + DDR_PHY_PHYINITSTATUS)); /* TODO: */
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Dataeye hardware training */
|
|
int ddr_hw_dataeye_read(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
|
|
unsigned int i;
|
|
int result;
|
|
|
|
ddr_training_cfg_init(cfg);
|
|
/* clear */
|
|
for (i = 0; i < byte_num; i++) {
|
|
ddr_write(0, base_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, i));
|
|
ddr_write(0, base_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, i));
|
|
ddr_write(0, base_phy + DDR_PHY_DXNRDQSDLY(i));
|
|
}
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
result = ddr_hw_training_process(cfg, PHY_PHYINITCTRL_RDET_EN);
|
|
|
|
ddr_hw_read_adj(cfg);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* DDR HW training control */
|
|
int ddr_hw_training_ctl(struct ddr_cfg_st *cfg)
|
|
{
|
|
int byte_idx;
|
|
int result = 0;
|
|
unsigned int temp = 0;
|
|
unsigned int item = cfg->cur_item;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
unsigned int dvrft_ctrl = ddr_read(base_phy + DDR_PHY_DVRFTCTRL);
|
|
struct rdqs_data_st *rdqs_st = (struct rdqs_data_st *)cfg->res_st;
|
|
|
|
if (!item || !rdqs_st)
|
|
return 0;
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
/* NOTE: not support array when boot */
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_CNT_RESET_START);
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_PLL);
|
|
|
|
/* save rdqs bdl after PHY_PHYINITCTRL_DLYMEAS_EN */
|
|
if (0 == cfg->rank_idx)
|
|
ddr_training_get_rdqs(cfg, &rdqs_st->origin);
|
|
|
|
for (byte_idx = 0; byte_idx < byte_num; byte_idx++) {
|
|
cfg->cur_byte = byte_idx;
|
|
ddr_bdl_adj(cfg);
|
|
}
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
temp = ddr_read(base_phy + 0x64);
|
|
ddr_write(temp & 0x0fffffff, base_phy + 0x64); /* ca odt disable */
|
|
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_DRAM_RESET);
|
|
ddr_write(temp, base_phy + 0x64); /* restore */
|
|
|
|
temp = ddr_read(base_phy + 0x48);
|
|
ddr_write(temp & 0xfffffffe, base_phy + 0x48); /* todo rank0 */
|
|
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_AC);
|
|
|
|
ddr_write(temp | 0x1, base_phy + 0x48); /* rank1 */
|
|
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_AC);
|
|
|
|
ddr_write(temp, base_phy + 0x48); /* restore */
|
|
|
|
/* ddr_training_delay(10000); */
|
|
result += ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_DRAM_INIT_EN);
|
|
} else {
|
|
#ifdef DDR_WRITE_DM_DISABLE
|
|
if (PHY_DRAMCFG_TYPE_DDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
temp = ddr_read(base_phy + 0xe0);
|
|
ddr_write((temp & 0xFBFFFFFF) | 0x8000000, base_phy + 0xe0); /* write dm disable */
|
|
}
|
|
#endif
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_DRAM_RESET);
|
|
}
|
|
result += ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_CAT_EN);
|
|
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_CS);
|
|
|
|
ddr_write(dvrft_ctrl & (~PHY_DVRFTCTRL_PDAEN_EN),
|
|
base_phy + DDR_PHY_DVRFTCTRL);
|
|
/* DDR_PHY_VREFTCTRL 31bit:1 do vref dram set twice */
|
|
ddr_write((ddr_read(base_phy + DDR_PHY_VREFTCTRL)
|
|
& (~(0x1 << PHY_VREFS_MRS_ENTER_BIT)))
|
|
| (0x1 << PHY_VREFS_MRS_ENTER_BIT),
|
|
base_phy + DDR_PHY_VREFTCTRL);
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ);
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ);
|
|
/* DDR_PHY_VREFTCTRL 31bit:0 do vref dram set once */
|
|
ddr_write(ddr_read(base_phy + DDR_PHY_VREFTCTRL)
|
|
& (~(0x1 << PHY_VREFS_MRS_ENTER_BIT)),
|
|
base_phy + DDR_PHY_VREFTCTRL);
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ);
|
|
ddr_write(dvrft_ctrl, base_phy + DDR_PHY_DVRFTCTRL);
|
|
|
|
result += ddr_hw_training_process(cfg, item & PHY_HW_GP_NORMAL);
|
|
|
|
#ifdef DDR_WRITE_DM_DISABLE
|
|
unsigned int temp1 = 0;
|
|
if (PHY_DRAMCFG_TYPE_DDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
ddr_write(temp, base_phy + 0xe0); /* restore */
|
|
temp = ddr_read(base_phy + 0x1e0);
|
|
temp1 = ddr_read(base_phy + 0x2c);
|
|
ddr_write(0x05555555, base_phy + 0x1e0); /* inti MR5 */
|
|
ddr_write(temp1 | 0x00004000 , base_phy + 0x2c); /* write dm disable */
|
|
result += ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_DRAM_INIT_EN);
|
|
ddr_write(temp, base_phy + 0x1e0); /* restore */
|
|
ddr_write(temp1, base_phy + 0x2c); /* restore */
|
|
}
|
|
#endif
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int ddr_hw_training_by_rank(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_DEBUG("PHY[%x][%x] Rank[%x] itme[%x]",
|
|
cfg->phy_idx, cfg->cur_phy, cfg->rank_idx, cfg->cur_item);
|
|
|
|
/* 0:PHY_TRAINCTRL0_DTR_RANK0, 1:PHY_TRAINCTRL0_DTR_RANK1 */
|
|
DDR_PHY_SWITCH_RANK(cfg->cur_phy, cfg->rank_idx);
|
|
return ddr_hw_training_ctl(cfg);
|
|
}
|
|
|
|
static int ddr_hw_training_by_phy(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
struct rdqs_data_st rdqs_data;
|
|
struct rdqs_data_st *rdqs_st = &rdqs_data;
|
|
struct ddr_timing_st timing_st;
|
|
unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num;
|
|
|
|
cfg->res_st = rdqs_st;
|
|
|
|
/* disable auto refresh */
|
|
ddr_training_save_timing(cfg, &timing_st);
|
|
|
|
for (i = 0; i < rank_num; i++) {
|
|
cfg->rank_idx = i;
|
|
cfg->cur_item = cfg->phy[cfg->phy_idx].rank[i].item_hw;
|
|
|
|
result += ddr_hw_training_by_rank(cfg);
|
|
|
|
if (DDR_SUPPORT_RANK_MAX != rank_num)
|
|
break;
|
|
|
|
/* save rank rdqs bdl */
|
|
ddr_training_get_rdqs(cfg, &(rdqs_st->rank[i]));
|
|
|
|
/* restore PHY_PHYINITCTRL_DLYMEAS_EN rdqs before training next rank */
|
|
if ((rank_num - 1) != i)
|
|
ddr_training_set_rdqs(cfg, &(rdqs_st->origin));
|
|
}
|
|
|
|
if (DDR_SUPPORT_RANK_MAX == rank_num) {
|
|
ddr_hw_training_adjust_rdqs(cfg, rdqs_st);
|
|
}
|
|
|
|
/* restore auto refresh */
|
|
ddr_training_restore_timing(cfg, &timing_st);
|
|
|
|
cfg->res_st = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* DDR hardware training */
|
|
int ddr_hw_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
struct tr_relate_reg reg;
|
|
|
|
/* save customer reg */
|
|
DDR_TRAINING_SAVE_REG_FUNC(®, 0x1);
|
|
ddr_boot_cmd_save_func(®);
|
|
|
|
for (i = 0; i < cfg->phy_num; i++) {
|
|
cfg->phy_idx = i;
|
|
cfg->cur_phy = cfg->phy[i].addr;
|
|
result += ddr_hw_training_by_phy(cfg);
|
|
}
|
|
/* restore customer reg */
|
|
DDR_TRAINING_RESTORE_REG_FUNC(®);
|
|
ddr_boot_cmd_restore_func(®);
|
|
|
|
return result;
|
|
}
|
|
#endif /* DDR_HW_TRAINING_CONFIG */
|
|
|
|
#define __mpr_training__
|
|
#ifdef DDR_MPR_TRAINING_CONFIG
|
|
/* Switch MPR function */
|
|
static void ddr_mpr_switch(unsigned int base_dmc, int val)
|
|
{
|
|
unsigned int sfc_cmd;
|
|
if (DDR_TRUE == val)
|
|
sfc_cmd = (DMC_CMD_MRS_MR3 << DMC_SFC_CMD_MRS_BIT)
|
|
| DMC_CMD_TYPE_LMR;
|
|
else
|
|
sfc_cmd = DMC_CMD_TYPE_LMR;
|
|
|
|
ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, DMC_BANK_MR3);
|
|
|
|
/* clear */
|
|
if (DDR_FALSE == val) {
|
|
ddr_write(0x0, base_dmc + DDR_DMC_SFCBANK);
|
|
ddr_write(0x0, base_dmc + DDR_DMC_SFCREQ);
|
|
}
|
|
}
|
|
|
|
/* Judge MPR data */
|
|
static int ddr_mpr_judge(unsigned int data1, unsigned int data2,
|
|
unsigned int data3, unsigned int data4,
|
|
unsigned int dq_index)
|
|
{
|
|
/* check byte */
|
|
if (-1 == dq_index) {
|
|
if (DDR_MPR_BYTE_MASK == data1 && 0x0 == data2
|
|
&& DDR_MPR_BYTE_MASK == data3 && 0x0 == data4)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
} else {
|
|
/* check DQ */
|
|
data1 = (data1 >> dq_index) & DDR_MPR_BIT_MASK;
|
|
data2 = (data2 >> dq_index) & DDR_MPR_BIT_MASK;
|
|
data3 = (data3 >> dq_index) & DDR_MPR_BIT_MASK;
|
|
data4 = (data4 >> dq_index) & DDR_MPR_BIT_MASK;
|
|
if (DDR_MPR_BIT_MASK == data1 && 0x0 == data2
|
|
&& DDR_MPR_BIT_MASK == data3 && 0x0 == data4)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Extract MPR read data to judge */
|
|
static int ddr_mpr_extract(struct ddr_cfg_st *cfg,
|
|
unsigned int offset0, unsigned int offset1,
|
|
unsigned int offset2, unsigned int offset3)
|
|
{
|
|
unsigned int data1, data2, data3, data4;
|
|
unsigned int base_dmc = cfg->cur_dmc;
|
|
unsigned int byte_index = cfg->cur_byte;
|
|
|
|
data1 = ddr_read(base_dmc + offset0); /* [127:96] or [255:224] */
|
|
data2 = ddr_read(base_dmc + offset1); /* [95:64] or [223:192] */
|
|
data3 = ddr_read(base_dmc + offset2); /* [63:32] or [191:160] */
|
|
data4 = ddr_read(base_dmc + offset3); /* [31:0] or [159:128] */
|
|
|
|
DDR_INFO("byte[%x] data[%x=%x][%x=%x][%x=%x][%x=%x]",
|
|
byte_index,
|
|
base_dmc + offset0, data1, base_dmc + offset1, data2,
|
|
base_dmc + offset2, data3, base_dmc + offset3, data4);
|
|
|
|
if (DDR_PHY_BYTE_MAX == GET_BYTE_NUM(cfg)) {
|
|
/* four byte: data1[0xFFFFFFFF] data2[0x00000000]
|
|
data3[0xFFFFFFFF] data4[0x00000000] */
|
|
data1 = (data1 >> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
data2 = (data2 >> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
data3 = (data3 >> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
data4 = (data4 >> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
} else {
|
|
/* two byte: data1[0xFFFF0000] data2[0xFFFF0000]
|
|
data3[0xFFFF0000] data4[0xFFFF0000] */
|
|
data1 = ((data1 >> DDR_MPR_BYTE_BIT) >> (byte_index << 3))
|
|
& DDR_MPR_BYTE_MASK;
|
|
data2 = (data2 >> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
data3 = ((data3 >> DDR_MPR_BYTE_BIT) >> (byte_index << 3))
|
|
& DDR_MPR_BYTE_MASK;
|
|
data4 = (data4 >> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
if (ddr_mpr_judge(data1, data2, data3, data4, cfg->cur_dq))
|
|
return -1;
|
|
|
|
/* two byte need to swap data and check again */
|
|
data1 = ((ddr_read(base_dmc + DDR_DMC_SFC_RDATA1)
|
|
>> DDR_MPR_BYTE_BIT) >> (byte_index << 3))
|
|
& DDR_MPR_BYTE_MASK;
|
|
data2 = (ddr_read(base_dmc + DDR_DMC_SFC_RDATA0)
|
|
>> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
data3 = ((ddr_read(base_dmc + DDR_DMC_SFC_RDATA3)
|
|
>> DDR_MPR_BYTE_BIT) >> (byte_index << 3))
|
|
& DDR_MPR_BYTE_MASK;
|
|
data4 = (ddr_read(base_dmc + DDR_DMC_SFC_RDATA2)
|
|
>> (byte_index << 3)) & DDR_MPR_BYTE_MASK;
|
|
}
|
|
|
|
return ddr_mpr_judge(data1, data2, data3, data4, cfg->cur_dq);
|
|
}
|
|
|
|
/* Check MPR read data */
|
|
int ddr_mpr_check(struct ddr_cfg_st *cfg)
|
|
{
|
|
/* read data */
|
|
ddr_dmc_sfc_cmd(cfg->cur_dmc, DMC_CMD_TYPE_READ, 0x0, 0x0);
|
|
return DMC_MPR_CHECK_BIT_0_127(cfg);
|
|
}
|
|
|
|
/* Find RDQ via MPR */
|
|
static int ddr_mpr_find_rdq(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct training_data tmp_result;
|
|
struct training_data *training = &tmp_result;
|
|
unsigned int dq_num;
|
|
unsigned int win_num;
|
|
unsigned int def_dq, best_dq;
|
|
unsigned int byte_index, dq_index;
|
|
|
|
/* find rdq via mpr */
|
|
cfg->dq_check_type = DDR_CHECK_TYPE_MPR;
|
|
|
|
/* find rdq */
|
|
for (byte_index = 0;
|
|
byte_index < GET_BYTE_NUM(cfg); byte_index++) {
|
|
for (dq_index = 0; dq_index < DDR_PHY_BIT_NUM; dq_index++) {
|
|
dq_num = (byte_index << 3) + dq_index;
|
|
def_dq = ddr_phy_get_dq_bdl(cfg);
|
|
ddr_dataeye_find_dq(cfg, training);
|
|
win_num = training->ddr_bit_best[dq_num]
|
|
>> DDR_DATAEYE_RESULT_BIT;
|
|
best_dq = training->ddr_bit_best[dq_num]
|
|
& DDR_DATAEYE_RESULT_MASK;
|
|
if (win_num > 0)
|
|
ddr_phy_set_dq_bdl(cfg, best_dq);
|
|
else {
|
|
/* In normal case, not reach here */
|
|
/* restore default value */
|
|
ddr_phy_set_dq_bdl(cfg, def_dq);
|
|
|
|
DDR_FATAL("PHY[%x] Byte[%x] DQ[%x] MPR fail",
|
|
cfg->cur_phy, byte_index, dq_index);
|
|
ddr_training_stat(DDR_ERR_MPR, cfg->cur_phy,
|
|
byte_index, dq_index);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Find RDQS via MPR */
|
|
static int ddr_mpr_find_rdqs(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int rdqs_start = 0;
|
|
unsigned int rdqs_end = PHY_RDQS_BDL_MASK;
|
|
unsigned int rdqs_mid;
|
|
unsigned int val, delay;
|
|
unsigned int count = 0;
|
|
int found = DDR_FALSE;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_index = cfg->cur_byte;
|
|
|
|
/* set rdq to middle value */
|
|
ddr_write(PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT, base_phy + DDR_PHY_DXNRDQNBDL0(cfg->rank_idx, byte_index));
|
|
ddr_write(PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT, base_phy + DDR_PHY_DXNRDQNBDL1(cfg->rank_idx, byte_index));
|
|
|
|
/* clear rdqs */
|
|
delay = ddr_read(base_phy + DDR_PHY_DXNRDQSDLY(byte_index)) >> PHY_RDQS_BDL_BIT;
|
|
rdqs_mid = delay; /* if not found, restore default value */
|
|
delay = delay & (~PHY_RDQS_BDL_MASK);
|
|
|
|
/* find rdqs */
|
|
for (val = 0; val <= PHY_RDQS_BDL_MASK; val++) {
|
|
ddr_write(delay | (val << PHY_RDQS_BDL_BIT),
|
|
base_phy + DDR_PHY_DXNRDQSDLY(byte_index));
|
|
ddr_phy_cfg_update(base_phy);
|
|
/* check ok */
|
|
if (!ddr_mpr_check(cfg)) {
|
|
if (DDR_FALSE == found) {
|
|
rdqs_start = val; /* found start value */
|
|
count++;
|
|
if (DDR_MPR_RDQS_FIND_TIMES == count)
|
|
found = DDR_TRUE;
|
|
}
|
|
} else {
|
|
if (DDR_TRUE == found) {
|
|
rdqs_end = val; /* found end value */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DDR_TRUE == found) {
|
|
rdqs_mid = ((rdqs_end - rdqs_start) >> 1) + rdqs_start;
|
|
DDR_INFO("PHY[%x] Byte[%x] rdqs_middle[%x]",
|
|
base_phy, byte_index, rdqs_mid);
|
|
DDR_INFO("rdqs_start[%x] rdqs_end[%x]",
|
|
rdqs_start, rdqs_end);
|
|
} else {
|
|
DDR_FATAL("PHY[%x] Byte[%x] not find RDQS, restore.",
|
|
base_phy, byte_index);
|
|
ddr_training_stat(DDR_ERR_MPR, base_phy,
|
|
byte_index, -1);
|
|
}
|
|
|
|
ddr_write(delay | (rdqs_mid << PHY_RDQS_BDL_BIT), base_phy + DDR_PHY_DXNRDQSDLY(byte_index));
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
return ((DDR_TRUE == found) ? 0 : -1);
|
|
}
|
|
|
|
/* Multi Purpose Register(MPR) */
|
|
int ddr_mpr_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
int i;
|
|
int result = 0;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
unsigned int mr0;
|
|
unsigned int sfc_cmd;
|
|
unsigned int base_dmc = cfg->cur_dmc;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
DDR_DEBUG("DDR MPR training.");
|
|
|
|
/* set DDR bust */
|
|
if (DDR_PHY_BYTE_MAX == byte_num) {
|
|
mr0 = (ddr_read(base_phy + DDR_PHY_MODEREG01)
|
|
& DMC_MRS_MASK)
|
|
& (~DMC_MR0_BL_MASK);
|
|
sfc_cmd = ((mr0 | DMC_MR0_BL_BUST4)
|
|
<< DMC_SFC_CMD_MRS_BIT)
|
|
| DMC_CMD_TYPE_LMR;
|
|
ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, 0x0);
|
|
}
|
|
|
|
/* precharge all */
|
|
ddr_dmc_sfc_cmd(base_dmc, DMC_CMD_TYPE_PRECHARGE_ALL, 0x0, 0x0);
|
|
|
|
/* enable MPR */
|
|
ddr_mpr_switch(base_dmc, DDR_TRUE);
|
|
|
|
/* find rdqs */
|
|
for (i = 0; i < byte_num; i++)
|
|
result += ddr_mpr_find_rdqs(cfg);
|
|
|
|
/* find rdq */
|
|
if (!result)
|
|
result = ddr_mpr_find_rdq(cfg);
|
|
|
|
/* disable MPR */
|
|
ddr_mpr_switch(base_dmc, DDR_FALSE);
|
|
|
|
/* restore DDR bust */
|
|
if (DDR_PHY_BYTE_MAX == byte_num) {
|
|
mr0 = (ddr_read(base_phy + DDR_PHY_MODEREG01)
|
|
& DMC_MRS_MASK);
|
|
sfc_cmd = (mr0 << DMC_SFC_CMD_MRS_BIT)
|
|
| DMC_CMD_TYPE_LMR;
|
|
ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, 0x0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ddr_mpr_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct tr_relate_reg relate_reg;
|
|
int result = 0;
|
|
|
|
/* MPR training disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_MPR_MASK))
|
|
return 0;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_MPR_MASK);
|
|
result = ddr_mpr_training(cfg);
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#endif /* DDR_MPR_TRAINING_CONFIG */
|
|
|
|
#define __vref_training__
|
|
#ifdef DDR_VREF_TRAINING_CONFIG
|
|
#ifdef DDR_VREF_WITHOUT_BDL_CONFIG
|
|
/* Save dataeye dq bdl before vref training */
|
|
static void ddr_vref_save_bdl(struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data)
|
|
{
|
|
int i;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int rank = cfg->rank_idx;
|
|
unsigned int byte_index;
|
|
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
byte_index = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
if (DDR_MODE_WRITE == cfg->cur_mode) {
|
|
dq_data->dq03[i] = ddr_read(base_phy + DDR_PHY_DXNWDQNBDL0(rank, byte_index));
|
|
dq_data->dq47[i] = ddr_read(base_phy + DDR_PHY_DXNWDQNBDL1(rank, byte_index));
|
|
dq_data->wdm[i] = ddr_read(base_phy + DDR_PHY_DXNWDQNBDL2(rank, byte_index));
|
|
} else {
|
|
dq_data->dq03[i] = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL0(rank, byte_index));
|
|
dq_data->dq47[i] = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL1(rank, byte_index));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Restore dataeye dq bdl after vref training */
|
|
static void ddr_vref_restore_bdl(struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data)
|
|
{
|
|
int i;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int rank = cfg->rank_idx;
|
|
unsigned int byte_index;
|
|
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
byte_index = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
if (DDR_MODE_WRITE == cfg->cur_mode) {
|
|
ddr_write(dq_data->dq03[i], base_phy + DDR_PHY_DXNWDQNBDL0(rank, byte_index));
|
|
ddr_write(dq_data->dq47[i], base_phy + DDR_PHY_DXNWDQNBDL1(rank, byte_index));
|
|
ddr_write(dq_data->wdm[i], base_phy + DDR_PHY_DXNWDQNBDL2(rank, byte_index));
|
|
} else {
|
|
ddr_write(dq_data->dq03[i], base_phy + DDR_PHY_DXNRDQNBDL0(rank, byte_index));
|
|
ddr_write(dq_data->dq47[i], base_phy + DDR_PHY_DXNRDQNBDL1(rank, byte_index));
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
static void ddr_vref_save_bdl(struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data)
|
|
{
|
|
}
|
|
static void ddr_vref_restore_bdl(struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data)
|
|
{
|
|
}
|
|
#endif /* DDR_VREF_WITHOUT_BDL_CONFIG */
|
|
|
|
/* Set DDR Vref value */
|
|
static void ddr_vref_set(struct ddr_cfg_st *cfg, unsigned int val)
|
|
{
|
|
if (DDR_MODE_READ == cfg->cur_mode) { /* HOST vref */
|
|
DDR_PHY_VREF_HOST_SET(cfg->cur_phy, cfg->rank_idx, GET_BYTE_NUM(cfg), cfg->cur_byte, val); /* TODO */
|
|
} else { /* DRAM vref */
|
|
unsigned int auto_ref_timing =
|
|
ddr_read(cfg->cur_dmc + DDR_DMC_TIMING2);
|
|
/* disable auto refresh */
|
|
ddr_training_set_timing(cfg->cur_dmc,
|
|
auto_ref_timing & DMC_AUTO_TIMING_DIS);
|
|
|
|
/* DDR_PHY_VREFTCTRL 31bit:1 do vref dram set twice */
|
|
ddr_write((ddr_read(cfg->cur_phy + DDR_PHY_VREFTCTRL)
|
|
& (~(0x1 << PHY_VREFS_MRS_ENTER_BIT)))
|
|
| (0x1 << PHY_VREFS_MRS_ENTER_BIT),
|
|
cfg->cur_phy + DDR_PHY_VREFTCTRL);
|
|
DDR_PHY_VREF_DRAM_SET(cfg->cur_phy, val, cfg->cur_byte);
|
|
DDR_PHY_VREF_DRAM_SET(cfg->cur_phy, val, cfg->cur_byte);
|
|
/* DDR_PHY_VREFTCTRL 31bit:0 do vref dram set once */
|
|
ddr_write(ddr_read(cfg->cur_phy + DDR_PHY_VREFTCTRL)
|
|
& (~(0x1 << PHY_VREFS_MRS_ENTER_BIT)),
|
|
cfg->cur_phy + DDR_PHY_VREFTCTRL);
|
|
DDR_PHY_VREF_DRAM_SET(cfg->cur_phy, val, cfg->cur_byte);
|
|
|
|
/* enable auto refresh */
|
|
ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing);
|
|
}
|
|
DDR_INFO("byte[%x] mode[%x] set vref [%x]", cfg->cur_byte, cfg->cur_mode, val);
|
|
}
|
|
|
|
/* Get DDR Vref value */
|
|
static unsigned int ddr_vref_get(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int val = 0;
|
|
|
|
if (DDR_MODE_READ == cfg->cur_mode) { /* HOST vref */
|
|
DDR_PHY_VREF_HOST_GET(cfg->cur_phy, cfg->rank_idx, cfg->cur_byte, val);
|
|
} else { /* DRAM vref */
|
|
DDR_PHY_VREF_DRAM_GET(cfg->cur_phy, val, cfg->cur_byte);
|
|
}
|
|
DDR_INFO("byte[%x] mode[%x] get vref [%x]", cfg->cur_byte, cfg->cur_mode, val);
|
|
return val;
|
|
}
|
|
|
|
/* Get totol win number of training result */
|
|
static unsigned int ddr_vref_get_win(struct ddr_cfg_st *cfg,
|
|
struct training_data *training, int vref)
|
|
{
|
|
unsigned int vref_min = 0;
|
|
unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX;
|
|
int vref_set;
|
|
|
|
training->ddr_win_sum = 0;
|
|
|
|
if (DDR_MODE_READ == cfg->cur_mode) {
|
|
DDR_VREF_GET_HOST_MAX(cfg->rank_idx, vref_max);
|
|
}
|
|
|
|
if (vref < vref_min)
|
|
vref_set = vref_min;
|
|
else if (vref > vref_max)
|
|
vref_set = vref_max;
|
|
else
|
|
vref_set = vref;
|
|
|
|
ddr_vref_set(cfg, vref_set);
|
|
|
|
ddr_dataeye_deskew(cfg, training);
|
|
|
|
return training->ddr_win_sum;
|
|
}
|
|
|
|
/* Find the best vref which win number is max */
|
|
static unsigned int ddr_vref_find_best(struct ddr_cfg_st *cfg,
|
|
struct training_data *training, unsigned int vref, int step)
|
|
{
|
|
int cur_vref;
|
|
unsigned int best_vref;
|
|
unsigned int cur_win;
|
|
unsigned int max_win;
|
|
unsigned int lower_times = 0;
|
|
unsigned int vref_min = 0;
|
|
unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX;
|
|
|
|
if (DDR_MODE_READ == cfg->cur_mode) {
|
|
DDR_VREF_GET_HOST_MAX(cfg->rank_idx, vref_max);
|
|
}
|
|
|
|
max_win = 0;
|
|
cur_vref = vref + step;
|
|
|
|
if (vref < vref_min)
|
|
best_vref = vref_min;
|
|
else if (vref > vref_max)
|
|
best_vref = vref_max;
|
|
else
|
|
best_vref = vref;
|
|
|
|
/* find parabola vertex */
|
|
while (cur_vref >= vref_min
|
|
&& cur_vref <= vref_max) {
|
|
cur_win = ddr_vref_get_win(cfg, training, cur_vref);
|
|
DDR_DEBUG("byte[%x] vref[%x] win[%x] mode[%x]",
|
|
cfg->cur_byte, cur_vref, cur_win, cfg->cur_mode);
|
|
if (cur_win < max_win) {
|
|
lower_times++;
|
|
if (DDR_VREF_COMPARE_TIMES == lower_times) {
|
|
/* Continuous decline, mean found vertex */
|
|
break;
|
|
}
|
|
} else {
|
|
lower_times = 0;
|
|
max_win = cur_win;
|
|
best_vref = cur_vref;
|
|
}
|
|
cur_vref = cur_vref + step;
|
|
}
|
|
|
|
return best_vref;
|
|
}
|
|
|
|
/* DDR Vref calibrate and set the best value */
|
|
static void ddr_vref_cal(struct ddr_cfg_st *cfg, struct training_data *training)
|
|
{
|
|
unsigned int def_vref;
|
|
unsigned int best_vref;
|
|
unsigned int left_win;
|
|
unsigned int right_win;
|
|
|
|
def_vref = ddr_vref_get(cfg);
|
|
left_win = ddr_vref_get_win(cfg, training, def_vref - DDR_VREF_COMPARE_STEP);
|
|
right_win = ddr_vref_get_win(cfg, training, def_vref + DDR_VREF_COMPARE_STEP);
|
|
|
|
DDR_DEBUG("byte[%x] default vref[%x] win[%x][%x] mode[%x]",
|
|
cfg->cur_byte, def_vref, left_win, right_win, cfg->cur_mode);
|
|
|
|
/* With vref increments, WIN number is a parabola.
|
|
So firstly determine the result on left or right. */
|
|
/* parabola vertex */
|
|
if (left_win < right_win) { /* the result on right */
|
|
best_vref = ddr_vref_find_best(cfg, training, def_vref, 1);
|
|
} else if (left_win > right_win) { /* the result on left */
|
|
best_vref = ddr_vref_find_best(cfg, training, def_vref, -1);
|
|
} else {
|
|
/* when (left_win == right_win), check def_vref */
|
|
unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX;
|
|
if (DDR_MODE_READ == cfg->cur_mode) {
|
|
DDR_VREF_GET_HOST_MAX(cfg->rank_idx, vref_max);
|
|
}
|
|
|
|
if (def_vref < (vref_max >> 1))
|
|
best_vref = ddr_vref_find_best(cfg, training, def_vref, 1);
|
|
else
|
|
best_vref = ddr_vref_find_best(cfg, training, def_vref, -1);
|
|
}
|
|
|
|
|
|
DDR_DEBUG("byte[%x] best vref[%x] mode[%x]",
|
|
cfg->cur_byte, best_vref, cfg->cur_mode);
|
|
ddr_vref_set(cfg, best_vref);
|
|
}
|
|
|
|
int ddr_vref_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct training_data tmp_result;
|
|
struct training_data *training = &tmp_result;
|
|
struct tr_dq_data dq_data;
|
|
int result = 0;
|
|
int i;
|
|
|
|
DDR_DEBUG("DDR Vref[%x] training PHY[%x][%x] DMC[%x][%x] Rank[%x]",
|
|
cfg->cur_mode, cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx,
|
|
cfg->cur_dmc, cfg->rank_idx);
|
|
|
|
ddr_vref_save_bdl(cfg, &dq_data);
|
|
ddrtr_memset(training, 0, sizeof(struct training_data));
|
|
|
|
/* vref calibrate */
|
|
if (DDR_MODE_READ == cfg->cur_mode) {
|
|
/* only training byte0 and byte2 */
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
if (1 == cfg->cur_byte || 3 == cfg->cur_byte)
|
|
continue;
|
|
|
|
ddr_vref_cal(cfg, training);
|
|
}
|
|
} else {
|
|
unsigned int dram_type = cfg->phy[cfg->phy_idx].dram_type;
|
|
unsigned int bank_group = (ddr_read(cfg->cur_dmc
|
|
+ DDR_DMC_CFG_RNKVOL(cfg->rank_idx)) >> DMC_CFG_MEM_BG_BIT)
|
|
& DMC_CFG_MEM_BG_MASK;
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 != dram_type
|
|
&& PHY_DRAMCFG_TYPE_DDR4 != dram_type)
|
|
return 0;
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == dram_type)
|
|
bank_group = DMC_CFG_MEM_2BG; /* lpddr4 not training byte1 byte3 */
|
|
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
/* byte1 and byte3 bypass when 2 Bank Group */
|
|
if ((DMC_CFG_MEM_2BG == bank_group)
|
|
&& ((1 == i) || (3 == i)))
|
|
continue;
|
|
|
|
ddr_vref_cal(cfg, training);
|
|
}
|
|
}
|
|
|
|
#if !defined(DDR_VREF_WITHOUT_BDL_CONFIG) || defined(DDR_TRAINING_CMD)
|
|
/* dataeye deskew again on best vref. */
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */
|
|
result += ddr_dataeye_deskew(cfg, training);
|
|
}
|
|
#endif
|
|
|
|
ddr_vref_restore_bdl(cfg, &dq_data);
|
|
|
|
ddr_result_data_save(cfg, training);
|
|
|
|
return result;
|
|
}
|
|
|
|
int ddr_vref_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct tr_relate_reg relate_reg;
|
|
int result = 0;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_VREF_HOST_MASK);
|
|
ddr_training_switch_axi(cfg);
|
|
ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE);
|
|
cfg->dq_check_type = DDR_CHECK_TYPE_DDRT;
|
|
|
|
/* host vref training disable */
|
|
if (!ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_HOST_MASK)) {
|
|
cfg->cur_mode = DDR_MODE_READ;
|
|
result += ddr_vref_training(cfg);
|
|
}
|
|
|
|
/* dram vref training enable && DDR4 */
|
|
if (!ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_DRAM_MASK)) {
|
|
cfg->cur_mode = DDR_MODE_WRITE;
|
|
result += ddr_vref_training(cfg);
|
|
}
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_vref_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DDR vref training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_VREF_TRAINING_CONFIG */
|
|
|
|
#define __write_leveling__
|
|
#ifdef DDR_WL_TRAINING_CONFIG
|
|
static void ddr_bdl_add(unsigned int *raw, unsigned int val)
|
|
{
|
|
if (((*raw) + val) > PHY_BDL_MASK)
|
|
*raw = PHY_BDL_MASK;
|
|
else
|
|
*raw += val;
|
|
}
|
|
|
|
static void ddr_bdl_sub(unsigned int *raw, unsigned int val)
|
|
{
|
|
if ((*raw) > val)
|
|
*raw -= val;
|
|
else
|
|
*raw = 0;
|
|
}
|
|
|
|
/* DDR PHY DQ phase increase */
|
|
static void ddr_phase_inc(unsigned int *raw)
|
|
{
|
|
#if defined (DDR_PHY_T28_CONFIG) || defined(DDR_PHY_T16_CONFIG) \
|
|
|| defined (DDR_PHY_T12_V100_CONFIG) || defined (DDR_PHY_T12_V101_CONFIG)
|
|
if ((*raw) < (PHY_WDQS_PHASE_MASK - 1)) {
|
|
if (((*raw) & 0x3) == 0x2)
|
|
*raw += 0x2;
|
|
else
|
|
*raw += 0x1;
|
|
}
|
|
#else
|
|
if ((*raw) < PHY_WDQS_PHASE_MASK)
|
|
*raw += 0x1;
|
|
#endif
|
|
}
|
|
|
|
/* DDR PHY DQ phase decrease */
|
|
static void ddr_phase_dec(unsigned int *raw)
|
|
{
|
|
#if defined (DDR_PHY_T28_CONFIG) || defined(DDR_PHY_T16_CONFIG) \
|
|
|| defined (DDR_PHY_T12_V100_CONFIG) || defined (DDR_PHY_T12_V101_CONFIG)
|
|
if ((*raw) > 0x1) {
|
|
if (((*raw) & 0x3) == 0x3)
|
|
*raw -= 0x2;
|
|
else
|
|
*raw -= 0x1;
|
|
}
|
|
#else
|
|
if ((*raw) > 0x0)
|
|
*raw -= 0x1;
|
|
#endif
|
|
}
|
|
|
|
/* DQ bdl add or sub */
|
|
static void ddr_dq_bdl_operate(unsigned int base_phy,
|
|
unsigned int addr_offset, unsigned int val, unsigned int is_add)
|
|
{
|
|
unsigned int tmp;
|
|
unsigned int dq_bdl[DDR_PHY_REG_DQ_NUM];
|
|
int i;
|
|
|
|
tmp = ddr_read(base_phy + addr_offset);
|
|
dq_bdl[0] = (tmp >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK;
|
|
dq_bdl[1] = (tmp >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK;
|
|
dq_bdl[2] = (tmp >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK;
|
|
dq_bdl[3] = (tmp >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK;
|
|
|
|
for (i = 0; i < DDR_PHY_REG_DQ_NUM; i++) {
|
|
if (is_add)
|
|
ddr_bdl_add(&dq_bdl[i], val);
|
|
else
|
|
ddr_bdl_sub(&dq_bdl[i], val);
|
|
}
|
|
|
|
tmp = (dq_bdl[3] << PHY_BDL_DQ3_BIT) + (dq_bdl[2] << PHY_BDL_DQ2_BIT)
|
|
+ (dq_bdl[1] << PHY_BDL_DQ1_BIT) + (dq_bdl[0] << PHY_BDL_DQ0_BIT);
|
|
ddr_write(tmp, base_phy + addr_offset);
|
|
}
|
|
|
|
/* Disable or enable DDR write leveling mode */
|
|
static void ddr_wl_switch(unsigned int base_dmc, unsigned int base_phy,
|
|
int val)
|
|
{
|
|
unsigned int mr1_raw;
|
|
unsigned int sfc_cmd;
|
|
unsigned int sfc_bank;
|
|
|
|
/* Set Rank = 0, Cmd = MRS, No Precharch CMD */
|
|
mr1_raw = ddr_read(base_phy + DDR_PHY_MODEREG01)
|
|
>> PHY_MODEREG01_MR1_BIT;
|
|
sfc_cmd = DMC_CMD_TYPE_LMR;
|
|
sfc_bank = DMC_BANK_MR1;
|
|
|
|
if (DDR_TRUE == val) { /* enable DDR wl */
|
|
/* Set A7 = 1 */
|
|
sfc_cmd += (mr1_raw | DMC_CMD_MRS_A7) << DMC_SFC_CMD_MRS_BIT;
|
|
} else {
|
|
|
|
/* Set A7 = 0 */
|
|
sfc_cmd += (mr1_raw & ((~DMC_CMD_MRS_A7) & DMC_CMD_MRS_MASK))
|
|
<< DMC_SFC_CMD_MRS_BIT;
|
|
}
|
|
|
|
ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, sfc_bank);
|
|
|
|
/* clear */
|
|
if (DDR_FALSE == val) {
|
|
ddr_write(0x0, base_dmc + DDR_DMC_SFCBANK);
|
|
ddr_write(0x0, base_dmc + DDR_DMC_SFCREQ);
|
|
}
|
|
|
|
/* phy sw write leveling mode */
|
|
ddr_write(val, base_phy + DDR_PHY_SWTMODE);
|
|
}
|
|
|
|
#ifdef DDR_WL_DATAEYE_ADJUST_CONFIG
|
|
/* Adjust dataeye WDQ after Write leveling */
|
|
static void ddr_wl_wdq_adjust(struct ddr_cfg_st *cfg,
|
|
struct ddr_delay_st *wdqs_new, struct ddr_delay_st *wdqs_old)
|
|
{
|
|
unsigned int val;
|
|
int i;
|
|
unsigned int phase_adj, bdl_adj = 0; /* for write dataeye */
|
|
unsigned int wdm_bdl;
|
|
unsigned int wdq_phase;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
unsigned int rank_index = cfg->rank_idx;
|
|
|
|
DDR_DEBUG("DDR WL write adjust.");
|
|
|
|
/* check wl write adjust bypass bit */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_ADJ_MASK))
|
|
return;
|
|
|
|
/* adjust wdq phase, wdq bdl, wdm bdl */
|
|
for (i = 0; i < byte_num; i++) {
|
|
if (wdqs_new->phase[i] == wdqs_old->phase[i]
|
|
&& wdqs_new->bdl[i] == wdqs_old->bdl[i]) {
|
|
continue;
|
|
}
|
|
|
|
phase_adj = 0;
|
|
wdq_phase = (ddr_read(base_phy + DDR_PHY_DXNWDQDLY(rank_index, i))
|
|
>> PHY_WDQ_PHASE_BIT)
|
|
& PHY_WDQ_PHASE_MASK;
|
|
wdm_bdl = (ddr_read(base_phy + DDR_PHY_DXNWDQNBDL2(rank_index, i))
|
|
>> PHY_WDM_BDL_BIT)
|
|
& PHY_BDL_MASK;
|
|
|
|
if (wdqs_new->bdl[i] > wdqs_old->bdl[i]) {
|
|
val = wdqs_new->bdl[i] - wdqs_old->bdl[i];
|
|
phase_adj = val >> DDR_BDL_PHASE_REL;
|
|
wdq_phase = wdq_phase + phase_adj;
|
|
|
|
if (wdq_phase > PHY_WDQ_PHASE_MASK)
|
|
wdq_phase = PHY_WDQ_PHASE_MASK;
|
|
|
|
/* adjust wdq bdl and dm bdl in opposite direction */
|
|
bdl_adj = phase_adj << DDR_BDL_PHASE_REL;
|
|
ddr_dq_bdl_operate(base_phy, DDR_PHY_DXNWDQNBDL0(rank_index, i),
|
|
bdl_adj, DDR_FALSE);
|
|
ddr_dq_bdl_operate(base_phy, DDR_PHY_DXNWDQNBDL1(rank_index, i),
|
|
bdl_adj, DDR_FALSE);
|
|
ddr_bdl_sub(&wdm_bdl, bdl_adj);
|
|
|
|
} else if (wdqs_new->bdl[i] < wdqs_old->bdl[i]) {
|
|
val = wdqs_old->bdl[i] - wdqs_new->bdl[i];
|
|
phase_adj = val >> DDR_BDL_PHASE_REL;
|
|
wdq_phase = (wdq_phase > phase_adj)
|
|
? (wdq_phase - phase_adj) : 0;
|
|
|
|
/* adjust wdq bdl and dm bdl in opposite direction */
|
|
bdl_adj = phase_adj << DDR_BDL_PHASE_REL;
|
|
ddr_dq_bdl_operate(base_phy, DDR_PHY_DXNWDQNBDL0(rank_index, i),
|
|
bdl_adj, DDR_TRUE);
|
|
ddr_dq_bdl_operate(base_phy, DDR_PHY_DXNWDQNBDL1(rank_index, i),
|
|
bdl_adj, DDR_TRUE);
|
|
ddr_bdl_add(&wdm_bdl, bdl_adj);
|
|
}
|
|
|
|
DDR_INFO("Byte[%x] WDQ adjust phase[%x] bdl[%x]",
|
|
i, phase_adj, bdl_adj);
|
|
|
|
ddr_write(wdq_phase << PHY_WDQ_PHASE_BIT,
|
|
base_phy + DDR_PHY_DXNWDQDLY(rank_index, i));
|
|
ddr_write(wdm_bdl << PHY_WDM_BDL_BIT, base_phy + DDR_PHY_DXNWDQNBDL2(rank_index, i));
|
|
}
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
}
|
|
#endif /* DDR_WL_DATAEYE_ADJUST_CONFIG */
|
|
|
|
/* Sync WDQ phase, WDQ bdl, WDM bdl, OEN bdl, WDQ SOE bdl by WDQS value */
|
|
static void ddr_wl_bdl_sync(struct ddr_cfg_st *cfg,
|
|
struct ddr_delay_st *wdqs_new, struct ddr_delay_st *wdqs_old)
|
|
{
|
|
unsigned int tmp;
|
|
unsigned int val;
|
|
int i;
|
|
|
|
unsigned int oen_bdl, wdqsoe_bdl, wdm_bdl;
|
|
unsigned int wdq_phase;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
unsigned int rank_index = cfg->rank_idx;
|
|
|
|
/* sync wdq phase, wdq bdl, wdm bdl, oen bdl, wdq soe bdl */
|
|
for (i = 0; i < byte_num; i++) {
|
|
if (wdqs_new->phase[i] == wdqs_old->phase[i]
|
|
&& wdqs_new->bdl[i] == wdqs_old->bdl[i]) {
|
|
continue;
|
|
}
|
|
|
|
DDR_DEBUG("Byte[%x] new[%x][%x] old[%x][%x]", i,
|
|
wdqs_new->phase[i], wdqs_new->bdl[i],
|
|
wdqs_old->phase[i], wdqs_old->bdl[i]);
|
|
|
|
/* wdq phase */
|
|
wdq_phase = (ddr_read(base_phy + DDR_PHY_DXNWDQDLY(rank_index, i))
|
|
>> PHY_WDQ_PHASE_BIT)
|
|
& PHY_WDQ_PHASE_MASK;
|
|
/* always new_phase >= old_phase */
|
|
wdq_phase = wdq_phase
|
|
+ (wdqs_new->phase[i] - wdqs_old->phase[i]);
|
|
|
|
/* bdl */
|
|
tmp = ddr_read(base_phy + DDR_PHY_DXNOEBDL(rank_index, i));
|
|
oen_bdl = (tmp >> PHY_OEN_BDL_BIT) & PHY_BDL_MASK;
|
|
wdqsoe_bdl = (tmp >> PHY_WDQSOE_BDL_BIT) & PHY_BDL_MASK;
|
|
wdm_bdl = (ddr_read(base_phy + DDR_PHY_DXNWDQNBDL2(rank_index, i))
|
|
>> PHY_WDM_BDL_BIT) & PHY_BDL_MASK;
|
|
|
|
if (wdqs_new->bdl[i] > wdqs_old->bdl[i]) {
|
|
val = wdqs_new->bdl[i] - wdqs_old->bdl[i];
|
|
ddr_dq_bdl_operate(base_phy,
|
|
DDR_PHY_DXNWDQNBDL0(rank_index, i), val, DDR_TRUE);
|
|
ddr_dq_bdl_operate(base_phy,
|
|
DDR_PHY_DXNWDQNBDL1(rank_index, i), val, DDR_TRUE);
|
|
ddr_bdl_add(&oen_bdl, val);
|
|
ddr_bdl_add(&wdqsoe_bdl, val);
|
|
ddr_bdl_add(&wdm_bdl, val);
|
|
} else if (wdqs_new->bdl[i] < wdqs_old->bdl[i]) {
|
|
val = wdqs_old->bdl[i] - wdqs_new->bdl[i];
|
|
ddr_dq_bdl_operate(base_phy, DDR_PHY_DXNWDQNBDL0(rank_index, i),
|
|
val, DDR_FALSE);
|
|
ddr_dq_bdl_operate(base_phy, DDR_PHY_DXNWDQNBDL1(rank_index, i),
|
|
val, DDR_FALSE);
|
|
ddr_bdl_sub(&oen_bdl, val);
|
|
ddr_bdl_sub(&wdqsoe_bdl, val);
|
|
ddr_bdl_sub(&wdm_bdl, val);
|
|
}
|
|
|
|
if (wdq_phase > PHY_WDQ_PHASE_MASK)
|
|
wdq_phase = PHY_WDQ_PHASE_MASK;
|
|
|
|
ddr_write(wdq_phase << PHY_WDQ_PHASE_BIT,
|
|
base_phy + DDR_PHY_DXNWDQDLY(rank_index, i));
|
|
ddr_write((wdqsoe_bdl << PHY_WDQSOE_BDL_BIT) + (oen_bdl << PHY_OEN_BDL_BIT),
|
|
base_phy + DDR_PHY_DXNOEBDL(rank_index, i));
|
|
ddr_write((wdm_bdl << PHY_WDM_BDL_BIT), base_phy + DDR_PHY_DXNWDQNBDL2(rank_index, i));
|
|
}
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
}
|
|
|
|
/**
|
|
* Write leveling process.
|
|
* WL depend default WDQS phase value in register init table.
|
|
*/
|
|
static int ddr_wl_process(struct ddr_cfg_st *cfg,
|
|
unsigned int type, struct ddr_delay_st *wdqs)
|
|
{
|
|
int i, j;
|
|
unsigned int wl_result = 0;
|
|
unsigned int length;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
|
|
if (DDR_DELAY_PHASE == type)
|
|
length = PHY_WDQS_PHASE_MASK;
|
|
else
|
|
length = PHY_BDL_MASK;
|
|
|
|
/* find WDQS phase or bdl, assume CLK Delay > DQS Delay */
|
|
for (i = 0; i <= length; i++) {
|
|
ddr_phy_cfg_update(base_phy);
|
|
ddr_write(0x1, base_phy + DDR_PHY_SWTWLDQS);
|
|
DDR_ASM_DSB();
|
|
wl_result = ddr_read(base_phy + DDR_PHY_SWTRLT)
|
|
& PHY_SWTRLT_WL_MASK;
|
|
ddr_write(0x0, base_phy + DDR_PHY_SWTWLDQS);
|
|
|
|
if ((wl_result & ((1 << byte_num) - 1)) == ((1 << byte_num) - 1))
|
|
break;
|
|
|
|
for (j = 0; j < byte_num; j++) {
|
|
DDR_INFO("type[0x%x] byte[0x%x] phase[0x%x] bdl[0x%x] wl_result[0x%x]",
|
|
type, j, wdqs->phase[j], wdqs->bdl[j], wl_result);
|
|
if (!(wl_result & (1 << j))) {
|
|
if (DDR_DELAY_PHASE == type)
|
|
ddr_phase_inc(&wdqs->phase[j]);
|
|
else
|
|
wdqs->bdl[j] += DDR_WL_BDL_STEP;
|
|
|
|
ddr_write((wdqs->phase[j] << PHY_WDQS_PHASE_BIT)
|
|
+ (wdqs->bdl[j] << PHY_WDQS_BDL_BIT),
|
|
base_phy + DDR_PHY_DXWDQSDLY(cfg->rank_idx, j));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i > length) { /* wl error, not find wdqs delay */
|
|
if (DDR_DELAY_BDL == type) {
|
|
DDR_FATAL("PHY[%x] WL fail, result[%x]",
|
|
base_phy, wl_result);
|
|
for (j = 0; j < byte_num; j++)
|
|
if (!(wl_result & (1 << j)))
|
|
ddr_training_stat(DDR_ERR_WL,
|
|
base_phy, j, -1);
|
|
|
|
} else
|
|
DDR_DEBUG("PHY[%x] WL not found phase, result[%x]",
|
|
base_phy, wl_result);
|
|
|
|
return -1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Find WDQS delay, sync to WDQ delay and OE delay.
|
|
* WL depend default WDQS phase value in register init table.
|
|
*/
|
|
int ddr_write_leveling(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int i, tmp;
|
|
struct ddr_delay_st wdqs_old;
|
|
struct ddr_delay_st wdqs_new;
|
|
int result = 0;
|
|
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int base_dmc = cfg->cur_dmc;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
unsigned int rank_index = cfg->rank_idx;
|
|
|
|
DDR_DEBUG("DDR Write Leveling training.");
|
|
|
|
/* init wdqs */
|
|
for (i = 0; i < byte_num; i++) {
|
|
tmp = ddr_read(base_phy + DDR_PHY_DXWDQSDLY(rank_index, i));
|
|
|
|
wdqs_old.phase[i] = (tmp >> PHY_WDQS_PHASE_BIT)
|
|
& PHY_WDQS_PHASE_MASK;
|
|
wdqs_old.bdl[i] = (tmp >> PHY_WDQS_BDL_BIT) & PHY_BDL_MASK;
|
|
|
|
wdqs_new.phase[i] = wdqs_old.phase[i];
|
|
wdqs_new.bdl[i] = 0;
|
|
|
|
/* clear wdqs bdl */
|
|
ddr_write(wdqs_new.phase[i] << PHY_WDQS_PHASE_BIT,
|
|
base_phy + DDR_PHY_DXWDQSDLY(rank_index, i));
|
|
}
|
|
|
|
/* enable sw write leveling mode */
|
|
ddr_wl_switch(base_dmc, base_phy, DDR_TRUE);
|
|
|
|
/* find first WDQS phase, assume CLK delay > DQS delay. */
|
|
result = ddr_wl_process(cfg, DDR_DELAY_PHASE, &wdqs_new);
|
|
|
|
/* check phase result */
|
|
for (i = 0; i < byte_num; i++) {
|
|
/* find phase error, keep max value to find bdl. */
|
|
/* find phase ok, decrease to find bdl. */
|
|
if (!result)
|
|
ddr_phase_dec(&wdqs_new.phase[i]);
|
|
|
|
ddr_write(wdqs_new.phase[i] << PHY_WDQS_PHASE_BIT,
|
|
base_phy + DDR_PHY_DXWDQSDLY(rank_index, i));
|
|
}
|
|
|
|
/* find WDQS bdl */
|
|
result = ddr_wl_process(cfg, DDR_DELAY_BDL, &wdqs_new);
|
|
|
|
/* disable sw write leveling mode */
|
|
ddr_wl_switch(base_dmc, base_phy, DDR_FALSE);
|
|
|
|
if (result) {
|
|
/* restore default value when find WDQS fail */
|
|
for (i = 0; i < byte_num; i++) {
|
|
tmp = (wdqs_old.phase[i] << PHY_WDQS_PHASE_BIT)
|
|
+ (wdqs_old.bdl[i] << PHY_WDQS_BDL_BIT);
|
|
ddr_write(tmp, base_phy + DDR_PHY_DXWDQSDLY(rank_index, i));
|
|
}
|
|
ddr_phy_cfg_update(base_phy);
|
|
return -1;
|
|
}
|
|
|
|
/* sync delay */
|
|
ddr_wl_bdl_sync(cfg, &wdqs_new, &wdqs_old);
|
|
|
|
#ifdef DDR_WL_DATAEYE_ADJUST_CONFIG
|
|
/* adjust WDQ for dataeye */
|
|
ddr_wl_wdq_adjust(cfg, &wdqs_new, &wdqs_old);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int ddr_wl_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct tr_relate_reg relate_reg;
|
|
int result = 0;
|
|
|
|
/* write leveling disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_MASK))
|
|
return 0;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_WL_MASK);
|
|
|
|
result += ddr_write_leveling(cfg);
|
|
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_wl_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DDR WL training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_WL_TRAINING_CONFIG */
|
|
|
|
#define __gate_training__
|
|
#ifdef DDR_GATE_TRAINING_CONFIG
|
|
/* Find gate phase */
|
|
static int ddr_gate_find_phase(struct ddr_cfg_st *cfg,
|
|
struct ddr_delay_st *rdqsg)
|
|
{
|
|
int i;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
for (i = 0; i < GET_BYTE_NUM(cfg); i++) {
|
|
for (rdqsg->phase[i] = PHY_RDQSG_PHASE_MAX;
|
|
rdqsg->phase[i] > PHY_GATE_PHASE_MARGIN;
|
|
rdqsg->phase[i] -= PHY_RDQSG_PHASE_STEP) {
|
|
ddr_write(rdqsg->phase[i] << PHY_RDQSG_PHASE_BIT,
|
|
base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, i));
|
|
ddr_phy_cfg_update(base_phy);
|
|
if (0 == ddr_ddrt_test(DDRT_WR_COMPRARE_MODE, i, -1))
|
|
break;
|
|
}
|
|
if (rdqsg->phase[i] <= PHY_GATE_PHASE_MARGIN) {
|
|
/* find gate phase fail */
|
|
DDR_FATAL("find gate phase[%x] fail.",
|
|
rdqsg->phase[i]);
|
|
ddr_training_stat(DDR_ERR_GATING, base_phy, -1, -1);
|
|
return -1;
|
|
} else {
|
|
/* decrease one setp to find bdl */
|
|
rdqsg->phase[i] -= PHY_RDQSG_PHASE_STEP;
|
|
ddr_write(rdqsg->phase[i] << PHY_RDQSG_PHASE_BIT,
|
|
base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, i));
|
|
}
|
|
}
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
return 0;
|
|
}
|
|
|
|
static int ddr_gate_find_bdl(struct ddr_cfg_st *cfg,
|
|
struct ddr_delay_st *rdqsg)
|
|
{
|
|
int i, j;
|
|
unsigned int gate_result;
|
|
unsigned int tmp;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
|
|
unsigned int swtmode = ddr_read(base_phy + DDR_PHY_SWTMODE);
|
|
|
|
for (i = 0; i < byte_num; i++)
|
|
rdqsg->bdl[i] = 0;
|
|
|
|
/* enable phy sw gate training mode */
|
|
ddr_write(swtmode | (1 << PHY_SWTMODE_SW_GTMODE_BIT),
|
|
base_phy + DDR_PHY_SWTMODE);
|
|
|
|
for (i = 0; i < PHY_GATE_BDL_MAX; i++) {
|
|
ddr_phy_cfg_update(base_phy);
|
|
ddr_ddrt_test(DDRT_READ_ONLY_MODE, -1, -1);
|
|
gate_result = (ddr_read(base_phy + DDR_PHY_SWTRLT) >> 8)
|
|
& PHY_SWTRLT_GATE_MASK;
|
|
if (gate_result == ((1 << byte_num) - 1))
|
|
break;
|
|
|
|
for (j = 0; j < byte_num; j++) {
|
|
if (!(gate_result & (1 << j))) {
|
|
rdqsg->bdl[j] += DDR_GATE_BDL_STEP;
|
|
if (rdqsg->bdl[j] > PHY_BDL_MASK) {
|
|
tmp = ((rdqsg->bdl[j]
|
|
- PHY_BDL_MASK - 1)
|
|
<< PHY_RDQSG_TX_BDL_BIT)
|
|
+ (rdqsg->phase[j]
|
|
<< PHY_RDQSG_PHASE_BIT)
|
|
+ (PHY_BDL_MASK - 1);
|
|
} else {
|
|
tmp = (rdqsg->phase[j]
|
|
<< PHY_RDQSG_PHASE_BIT)
|
|
+ rdqsg->bdl[j];
|
|
}
|
|
ddr_write(tmp,
|
|
base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, j));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* disable phy sw gate training mode */
|
|
ddr_write(swtmode & (~(1 << PHY_SWTMODE_SW_GTMODE_BIT)),
|
|
base_phy + DDR_PHY_SWTMODE);
|
|
|
|
if (i == PHY_GATE_BDL_MAX) { /* find gate bdl fail */
|
|
DDR_FATAL("PHY[%x] find gate bdl fail. result[%x]",
|
|
base_phy, gate_result);
|
|
for (j = 0; j < byte_num; j++)
|
|
if (!(gate_result & (1 << j)))
|
|
ddr_training_stat(DDR_ERR_GATING,
|
|
base_phy, j, -1);
|
|
return -1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
int ddr_gate_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int i, tmp;
|
|
unsigned int byte_num;
|
|
struct ddr_delay_st rdqsg;
|
|
unsigned int def_delay[DDR_PHY_BYTE_MAX];
|
|
int result;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
DDR_DEBUG("DDR Gate training.");
|
|
|
|
byte_num = GET_BYTE_NUM(cfg);
|
|
|
|
for (i = 0; i < byte_num; i++)
|
|
def_delay[i] = ddr_read(base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, i));
|
|
|
|
/* find phase first */
|
|
result = ddr_gate_find_phase(cfg, &rdqsg);
|
|
|
|
/* find bdl */
|
|
if (!result)
|
|
result = ddr_gate_find_bdl(cfg, &rdqsg);
|
|
|
|
/* set new phase */
|
|
if (!result) {
|
|
for (i = 0; i < byte_num; i++) {
|
|
rdqsg.phase[i] -= PHY_GATE_PHASE_MARGIN;
|
|
tmp = ddr_read(base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, i));
|
|
tmp &= ~(PHY_RDQSG_PHASE_MASK << PHY_RDQSG_PHASE_BIT);
|
|
tmp |= rdqsg.phase[i] << PHY_RDQSG_PHASE_BIT;
|
|
ddr_write(tmp, base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, i));
|
|
}
|
|
} else {
|
|
/* restore default value */
|
|
for (i = 0; i < byte_num; i++)
|
|
ddr_write(def_delay[i],
|
|
base_phy + DDR_PHY_DXNRDQSGDLY(cfg->rank_idx, i));
|
|
}
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
return 0; /* use default value and not reset */
|
|
}
|
|
|
|
int ddr_gating_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct tr_relate_reg relate_reg;
|
|
int result = 0;
|
|
|
|
/* gate training disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_GATE_MASK)) {
|
|
/* check hardware gating */
|
|
if (ddr_read(cfg->cur_phy + DDR_PHY_PHYINITSTATUS)
|
|
& PHY_INITSTATUS_GT_MASK) {
|
|
DDR_FATAL("PHY[%x] hw gating fail.", cfg->cur_phy);
|
|
ddr_training_stat(DDR_ERR_HW_GATING,
|
|
cfg->cur_phy, -1, -1);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_GATE_MASK);
|
|
|
|
ddr_training_switch_axi(cfg);
|
|
ddr_ddrt_init(cfg, DDR_DDRT_MODE_GATE);
|
|
result += ddr_gate_training(cfg);
|
|
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_gating_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DDR gate training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_GATE_TRAINING_CONFIG */
|
|
|
|
#define __ac_training__
|
|
#ifdef DDR_AC_TRAINING_CONFIG
|
|
/**
|
|
* Get clk value.
|
|
* Assume clk0 and clk1 is the same.
|
|
*/
|
|
static int ddr_ac_get_clk(unsigned int base_phy)
|
|
{
|
|
unsigned int val;
|
|
unsigned int ac_phy_ctl;
|
|
/* Static register have to read two times to get the right value. */
|
|
ac_phy_ctl = ddr_read(base_phy + DDR_PHY_ACPHYCTL7);
|
|
ac_phy_ctl = ddr_read(base_phy + DDR_PHY_ACPHYCTL7);
|
|
/* halft_dramclk0 */
|
|
val = (ac_phy_ctl >> PHY_ACPHY_DRAMCLK0_BIT)
|
|
& PHY_ACPHY_DRAMCLK_MASK;
|
|
val = (val << PHY_ACPHY_DRAMCLK_EXT_BIT)
|
|
| ((ac_phy_ctl >> PHY_ACPHY_DCLK0_BIT)
|
|
& PHY_ACPHY_DCLK_MASK);
|
|
return val;
|
|
}
|
|
|
|
/* Set clk0 and clk1 the same value */
|
|
static void ddr_ac_set_clk(unsigned int base_phy, unsigned int val)
|
|
{
|
|
unsigned int ac_phy_ctl, dramclk, dclk;
|
|
dclk = val & PHY_ACPHY_DCLK_MASK;
|
|
dramclk = (val >> PHY_ACPHY_DRAMCLK_EXT_BIT)
|
|
& PHY_ACPHY_DRAMCLK_MASK;
|
|
/* Static register have to read two times to get the right value. */
|
|
ac_phy_ctl = ddr_read(base_phy + DDR_PHY_ACPHYCTL7);
|
|
ac_phy_ctl = ddr_read(base_phy + DDR_PHY_ACPHYCTL7);
|
|
/* clear cp1p_dclk0 */
|
|
ac_phy_ctl &= (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK0_BIT));
|
|
/* clear ck2p_dclk1 */
|
|
ac_phy_ctl &= (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK1_BIT));
|
|
/* clear halft_dramclk0 */
|
|
ac_phy_ctl &= (~(PHY_ACPHY_DRAMCLK_MASK << PHY_ACPHY_DRAMCLK0_BIT));
|
|
/* clear halft_dramclk1 */
|
|
ac_phy_ctl &= (~(PHY_ACPHY_DRAMCLK_MASK << PHY_ACPHY_DRAMCLK1_BIT));
|
|
|
|
ac_phy_ctl |= (dclk << PHY_ACPHY_DCLK0_BIT); /* set cp1p_dclk0 */
|
|
ac_phy_ctl |= (dclk << PHY_ACPHY_DCLK1_BIT); /* set cp2p_dclk1 */
|
|
/* set halft_dramclk0 */
|
|
ac_phy_ctl |= (dramclk << PHY_ACPHY_DRAMCLK0_BIT);
|
|
/* set halft_dramclk1 */
|
|
ac_phy_ctl |= (dramclk << PHY_ACPHY_DRAMCLK1_BIT);
|
|
ddr_write(ac_phy_ctl, base_phy + DDR_PHY_ACPHYCTL7);
|
|
}
|
|
|
|
/**
|
|
* Get cs bdl value.
|
|
* Assume cs0 and cs 1 is the same.
|
|
*/
|
|
static int ddr_ac_get_cs(unsigned int base_phy)
|
|
{
|
|
return (ddr_read(base_phy + DDR_PHY_ACCMDBDL2) >> 1) & PHY_BDL_MASK;
|
|
}
|
|
|
|
/* Set CS value */
|
|
static void ddr_ac_set_cs(unsigned int base_phy, unsigned int val)
|
|
{
|
|
unsigned int ac_cmd_bdl;
|
|
ac_cmd_bdl = ddr_read(base_phy + DDR_PHY_ACCMDBDL2);
|
|
ac_cmd_bdl &= (~(PHY_BDL_MASK << PHY_ACCMD_CS0_BIT)); /* clear cs0_bdl */
|
|
ac_cmd_bdl &= (~(PHY_BDL_MASK << PHY_ACCMD_CS1_BIT)); /* clear cs1_bdl */
|
|
ac_cmd_bdl |= (val << PHY_ACCMD_CS0_BIT); /* set cs0_bdl */
|
|
ac_cmd_bdl |= (val << PHY_ACCMD_CS1_BIT); /* set cs1_bdl */
|
|
ddr_write(ac_cmd_bdl, base_phy + DDR_PHY_ACCMDBDL2);
|
|
}
|
|
|
|
static int ddr_ac_ddrt_test(unsigned int mask, unsigned int base_phy)
|
|
{
|
|
unsigned int regval;
|
|
unsigned int times = 0;
|
|
|
|
DDRT_REG_WRITE(mask | DDRT_CFG_START, DDR_REG_BASE_DDRT + DDRT_OP);
|
|
DDRT_REG_WRITE(0, DDR_REG_BASE_DDRT + DDRT_STATUS);
|
|
|
|
do {
|
|
regval = DDRT_REG_READ(DDR_REG_BASE_DDRT + DDRT_STATUS);
|
|
times++;
|
|
} while ((!(regval & DDRT_TEST_DONE_MASK))
|
|
&& (times < DDRT_WAIT_TIMEOUT));
|
|
|
|
if (times >= DDRT_WAIT_TIMEOUT) {
|
|
DDR_FATAL("DDRT wait timeout.");
|
|
ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, base_phy, -1, -1);
|
|
return -1;
|
|
}
|
|
|
|
/* DDRT_WRITE_ONLY_MODE */
|
|
if (DDRT_WRITE_ONLY_MODE == (mask & DDRT_TEST_MODE_MASK))
|
|
return 0;
|
|
|
|
/* DDRT_READ_ONLY_MODE */
|
|
if (regval & DDRT_TEST_PASS_MASK) /* No error occurred, test pass. */
|
|
return 0;
|
|
else
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Check CS value */
|
|
static int ddr_ac_check_cs(unsigned int base_phy, unsigned int def_cs,
|
|
unsigned int step)
|
|
{
|
|
ddr_ac_set_cs(base_phy, def_cs + step);
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
ddr_ac_ddrt_test(DDRT_WRITE_ONLY_MODE, base_phy);
|
|
|
|
ddr_ac_set_cs(base_phy, def_cs); /* restore default to check */
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
return ddr_ac_ddrt_test(DDRT_READ_ONLY_MODE, base_phy);
|
|
}
|
|
|
|
/* Check CLK value */
|
|
static int ddr_ac_check_clk(struct ddr_cfg_st *cfg, unsigned int def_clk,
|
|
struct ddr_delay_st *def_phase,
|
|
unsigned int step)
|
|
{
|
|
int i;
|
|
unsigned int wdqs_phase_range, wdq_phase_range, phase_range;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
|
|
/* set new value */
|
|
ddr_ac_set_clk(base_phy, def_clk + step);
|
|
for (i = 0; i < byte_num; i++) {
|
|
wdqs_phase_range = PHY_WDQS_PHASE_MASK
|
|
- ((def_phase->phase[i] >> PHY_WDQS_PHASE_BIT)
|
|
& PHY_WDQS_PHASE_MASK);
|
|
wdq_phase_range = PHY_WDQ_PHASE_MASK
|
|
- ((def_phase->bdl[i] >> PHY_WDQ_PHASE_BIT)
|
|
& PHY_WDQ_PHASE_MASK);
|
|
phase_range = (wdqs_phase_range < wdq_phase_range)
|
|
? wdqs_phase_range : wdq_phase_range;
|
|
phase_range = (phase_range < step) ? phase_range : step;
|
|
|
|
ddr_write(def_phase->phase[i]
|
|
+ (phase_range << PHY_WDQS_PHASE_BIT),
|
|
base_phy + DDR_PHY_DXWDQSDLY(cfg->rank_idx, i));
|
|
ddr_write(def_phase->bdl[i]
|
|
+ (phase_range << PHY_WDQ_PHASE_BIT),
|
|
base_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, i));
|
|
}
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
ddr_ac_ddrt_test(DDRT_WRITE_ONLY_MODE, base_phy);
|
|
|
|
/* restore default to check */
|
|
ddr_ac_set_clk(base_phy, def_clk);
|
|
for (i = 0; i < byte_num; i++) {
|
|
ddr_write(def_phase->phase[i],
|
|
base_phy + DDR_PHY_DXWDQSDLY(cfg->rank_idx, i));
|
|
ddr_write(def_phase->bdl[i],
|
|
base_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, i));
|
|
}
|
|
ddr_phy_cfg_update(base_phy);
|
|
|
|
return ddr_ac_ddrt_test(DDRT_READ_ONLY_MODE, base_phy);
|
|
}
|
|
|
|
/* Find CS difference */
|
|
static int ddr_ac_find_cs(unsigned int base_phy)
|
|
{
|
|
unsigned int def_cs, step;
|
|
|
|
def_cs = ddr_ac_get_cs(base_phy);
|
|
for (step = 1; step <= (PHY_BDL_MASK - def_cs); step++) {
|
|
if (ddr_ac_check_cs(base_phy, def_cs, step)) {
|
|
DDR_DEBUG("PHY[%x] default cs[%x], find diff_cs[%x]",
|
|
base_phy, def_cs, step);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return step;
|
|
}
|
|
|
|
/* Find CLK difference */
|
|
static int ddr_ac_find_clk(struct ddr_cfg_st *cfg)
|
|
{
|
|
int i;
|
|
unsigned int def_clk, step;
|
|
struct ddr_delay_st def_phase;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int byte_num = GET_BYTE_NUM(cfg);
|
|
|
|
def_clk = ddr_ac_get_clk(base_phy);
|
|
for (i = 0; i < byte_num; i++) {
|
|
/* WDQS phase */
|
|
def_phase.phase[i] = ddr_read(base_phy + DDR_PHY_DXWDQSDLY(cfg->rank_idx, i));
|
|
/* WDQ phase */
|
|
def_phase.bdl[i] = ddr_read(base_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, i));
|
|
}
|
|
|
|
for (step = 1; step <= (PHY_ACPHY_CLK_MAX - def_clk); step++) {
|
|
if (ddr_ac_check_clk(cfg, def_clk, &def_phase, step)) {
|
|
DDR_DEBUG("PHY[%x] default clk[%x], find diff_clk[%x]",
|
|
base_phy, def_clk, step);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return step;
|
|
}
|
|
|
|
/* DDR AC training */
|
|
int ddr_ac_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int diff_cs, diff_clk;
|
|
unsigned int clk_phase, cs_bdl, phase_tmp;
|
|
unsigned int byte_num;
|
|
unsigned int wdqs_phase, wdq_phase;
|
|
unsigned int wdqs_phase_range, wdq_phase_range, phase_range;
|
|
unsigned int def_clk, def_cs;
|
|
int i;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
|
|
DDR_DEBUG("DDR AC training.");
|
|
|
|
byte_num = GET_BYTE_NUM(cfg);
|
|
|
|
diff_cs = ddr_ac_find_cs(base_phy); /* setup time(bdl) */
|
|
diff_clk = ddr_ac_find_clk(cfg); /* hold time(phase) */
|
|
/* cs bdl transform to clk phase */
|
|
phase_tmp = diff_cs >> DDR_BDL_PHASE_REL;
|
|
|
|
if (diff_clk > phase_tmp) {
|
|
clk_phase = (diff_clk - phase_tmp) >> 1;
|
|
def_clk = ddr_ac_get_clk(base_phy);
|
|
|
|
/* set new value */
|
|
ddr_ac_set_clk(base_phy, def_clk + clk_phase);
|
|
for (i = 0; i < byte_num; i++) {
|
|
wdqs_phase = ddr_read(base_phy + DDR_PHY_DXWDQSDLY(cfg->rank_idx, i));
|
|
wdq_phase = ddr_read(base_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, i));
|
|
|
|
wdqs_phase_range = PHY_WDQS_PHASE_MASK
|
|
- ((wdqs_phase >> PHY_WDQS_PHASE_BIT)
|
|
& PHY_WDQS_PHASE_MASK);
|
|
wdq_phase_range = PHY_WDQ_PHASE_MASK
|
|
- ((wdq_phase >> PHY_WDQ_PHASE_BIT)
|
|
& PHY_WDQ_PHASE_MASK);
|
|
phase_range = (wdqs_phase_range < wdq_phase_range)
|
|
? wdqs_phase_range : wdq_phase_range;
|
|
phase_range = (phase_range < clk_phase)
|
|
? phase_range : clk_phase;
|
|
ddr_write(wdqs_phase
|
|
+ (phase_range << PHY_WDQS_PHASE_BIT),
|
|
base_phy + DDR_PHY_DXWDQSDLY(cfg->rank_idx, i));
|
|
ddr_write(wdq_phase
|
|
+ (phase_range << PHY_WDQ_PHASE_BIT),
|
|
base_phy + DDR_PHY_DXNWDQDLY(cfg->rank_idx, i));
|
|
}
|
|
DDR_DEBUG("PHY[%x] def clk[%x] add phase[%x]",
|
|
base_phy, def_clk, clk_phase);
|
|
} else {
|
|
def_cs = ddr_ac_get_cs(base_phy);
|
|
cs_bdl = 0;
|
|
if (diff_cs > (diff_clk << DDR_BDL_PHASE_REL))
|
|
cs_bdl = diff_cs - (diff_clk << DDR_BDL_PHASE_REL);
|
|
|
|
ddr_ac_set_cs(base_phy, def_cs + cs_bdl);
|
|
DDR_DEBUG("PHY[%x] def cs[%x] add bdl[%x]",
|
|
base_phy, def_cs, cs_bdl);
|
|
}
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
return 0;
|
|
}
|
|
|
|
int ddr_ac_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
struct tr_relate_reg relate_reg;
|
|
|
|
/* AC training disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_AC_MASK))
|
|
return 0;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_AC_MASK);
|
|
|
|
ddr_training_switch_axi(cfg);
|
|
ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE);
|
|
result += ddr_ac_training(cfg);
|
|
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_ac_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DDR AC training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_AC_TRAINING_CONFIG */
|
|
|
|
#define __lpca_training__
|
|
#ifdef DDR_LPCA_TRAINING_CONFIG
|
|
/* Reset address bdl training data */
|
|
static void ddr_lpca_reset(struct ca_data_st *data)
|
|
{
|
|
unsigned int index;
|
|
for (index = 0; index < DDR_PHY_CA_MAX; index++) {
|
|
data->left[index] = -1;
|
|
data->right[index] = -1;
|
|
}
|
|
|
|
data->min = PHY_ACADDR_BDL_MASK;
|
|
data->max = 0;
|
|
data->done = 0;
|
|
}
|
|
|
|
/* Get ca bit relation */
|
|
static void ddr_lpca_get_bit(struct ca_data_st *data)
|
|
{
|
|
unsigned int index;
|
|
//unsigned int swap_sel;
|
|
|
|
/* get ca bit in four register */
|
|
#if 0
|
|
for (index = 0; index < (DDR_PHY_CA_REG_MAX - 1); index++) {
|
|
ddr_write(index + 1, data->base_phy + DDR_PHY_CATSWAPINDEX);
|
|
swap_sel = ddr_read(data->base_phy + DDR_PHY_CATSWAPSEL);
|
|
|
|
data->bits[index * 2].bit_p =
|
|
swap_sel & PHY_CATSWAPSEL_BIT_MASK;
|
|
data->bits[index * 2].bit_n =
|
|
(swap_sel >> 8) & PHY_CATSWAPSEL_BIT_MASK;
|
|
data->bits[index * 2 + 1].bit_p =
|
|
(swap_sel >> 16) & PHY_CATSWAPSEL_BIT_MASK;
|
|
data->bits[index * 2 + 1].bit_n =
|
|
(swap_sel >> 24) & PHY_CATSWAPSEL_BIT_MASK;
|
|
}
|
|
#else
|
|
for (index = 0; index < (DDR_PHY_CA_REG_MAX - 1); index++) {
|
|
data->bits[index * 2].bit_p =index*4+ 0;
|
|
data->bits[index * 2].bit_n =index*4+ 1;
|
|
data->bits[index * 2 + 1].bit_p =index*4+ 2;
|
|
data->bits[index * 2 + 1].bit_n =index*4+ 3;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* set ca bit for ca4 and ca9
|
|
* ca4 = ca0, ca9 = ca5
|
|
*/
|
|
for (index = 8; index > 4; index--) {
|
|
data->bits[index].bit_p = data->bits[index - 1].bit_p;
|
|
data->bits[index].bit_n = data->bits[index - 1].bit_n;
|
|
}
|
|
|
|
data->bits[4].bit_p = data->bits[0].bit_p;
|
|
data->bits[4].bit_n = data->bits[0].bit_n;
|
|
data->bits[9].bit_p = data->bits[5].bit_p;
|
|
data->bits[9].bit_n = data->bits[5].bit_n;
|
|
|
|
#if defined(DDR_TRAINING_CMD)
|
|
for (index = 0; index < DDR_PHY_CA_MAX; index++) {
|
|
DDR_INFO("CA[%x] bit_p[%x]", index, data->bits[index].bit_p);
|
|
DDR_INFO("CA[%x] bit_n[%x]", index, data->bits[index].bit_n);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Get address bdl default value */
|
|
static void ddr_lpca_get_def(struct ca_data_st *data)
|
|
{
|
|
unsigned int index;
|
|
|
|
for (index = 0; index < DDR_PHY_CA_REG_MAX; index++)
|
|
data->def[index] = ddr_read(data->base_phy
|
|
+ DDR_PHY_ACADDRBDL(index));
|
|
}
|
|
|
|
/* Restore address bdl default value */
|
|
static void ddr_lpca_restore_def(struct ca_data_st *data)
|
|
{
|
|
unsigned int index;
|
|
|
|
for (index = 0; index < DDR_PHY_CA_REG_MAX; index++)
|
|
ddr_write(data->def[index], data->base_phy
|
|
+ DDR_PHY_ACADDRBDL(index));
|
|
|
|
ddr_phy_cfg_update(data->base_phy);
|
|
}
|
|
|
|
/* Set address bdl value */
|
|
static void ddr_lpca_set_bdl(unsigned int base_phy, unsigned int bdl)
|
|
{
|
|
unsigned int index;
|
|
for (index = 0; index < DDR_PHY_CA_REG_MAX; index++)
|
|
ddr_write(bdl | (bdl << PHY_ACADDRBDL_ADDR1_BIT),
|
|
base_phy + DDR_PHY_ACADDRBDL(index));
|
|
|
|
ddr_phy_cfg_update(base_phy);
|
|
}
|
|
|
|
/* Update address bdl value with training result */
|
|
static void ddr_lpca_update_bdl(struct ca_data_st *data)
|
|
{
|
|
unsigned int index;
|
|
unsigned int addr0, addr1;
|
|
|
|
for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) {
|
|
addr0 = (data->left[index * 2] + data->right[index * 2]) >> 1;
|
|
addr1 = (data->left[index * 2 + 1]
|
|
+ data->right[index * 2 + 1]) >> 1;
|
|
ddr_write(addr0 | (addr1 << PHY_ACADDRBDL_ADDR1_BIT),
|
|
data->base_phy + DDR_PHY_ACADDRBDL(index));
|
|
}
|
|
|
|
ddr_phy_cfg_update(data->base_phy);
|
|
}
|
|
|
|
/* Init data before training */
|
|
static void ddr_lpca_init(unsigned int base_dmc, unsigned int base_phy,
|
|
struct ca_data_st *data)
|
|
{
|
|
data->base_dmc = base_dmc;
|
|
data->base_phy = base_phy;
|
|
|
|
/* gat ca bit relation */
|
|
ddr_lpca_get_bit(data);
|
|
|
|
/* get ac addr bdl default value */
|
|
ddr_lpca_get_def(data);
|
|
|
|
/* reset training data */
|
|
ddr_lpca_reset(data);
|
|
}
|
|
|
|
/* Display training result */
|
|
static void ddr_lpca_display(struct ca_data_st *data)
|
|
{
|
|
#if defined(DDR_TRAINING_CMD)
|
|
unsigned int index;
|
|
|
|
DDR_DEBUG("CA phase[%x = %x]",
|
|
data->base_phy + DDR_PHY_ADDRPHBOUND,
|
|
ddr_read(data->base_phy + DDR_PHY_ADDRPHBOUND));
|
|
|
|
for (index = 0; index < DDR_PHY_CA_MAX; index++)
|
|
DDR_DEBUG("CA[%x] left[%x] right[%x]",
|
|
index, data->left[index], data->right[index]);
|
|
|
|
DDR_DEBUG("min[%x] max[%x] done[%x]",
|
|
data->min, data->max, data->done);
|
|
#endif
|
|
}
|
|
|
|
/* Wait lpca command done */
|
|
static void ddr_lpca_wait(volatile union U_PHY_CATCONFIG *ca)
|
|
{
|
|
unsigned int count = 0;
|
|
while (count < DDR_LPCA_WAIT_TIMEOUT) {
|
|
if (1 == ca->bits.sw_cat_dqvalid) {
|
|
ca->bits.sw_cat_dqvalid = 0; /* clear */
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* generally, count is 0 */
|
|
if (count >= DDR_LPCA_WAIT_TIMEOUT)
|
|
DDR_ERROR("LPCA wait timeout.");
|
|
}
|
|
|
|
/* Compare dq result and pattern */
|
|
static int ddr_lpca_compare(struct ca_bit_st *ca_bit,
|
|
unsigned int dq_result, unsigned int pattern_p,
|
|
unsigned int pattern_n, unsigned int index)
|
|
{
|
|
if (((dq_result >> ca_bit->bit_p) & 0x1)
|
|
!= ((pattern_p >> index) & 0x1))
|
|
return -1;
|
|
|
|
if (((dq_result >> ca_bit->bit_n) & 0x1)
|
|
!= ((pattern_n >> index) & 0x1))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Check each CA whether pass */
|
|
static void ddr_lpca_check(struct ca_data_st *data, unsigned int bdl,
|
|
unsigned int is_ca49)
|
|
{
|
|
unsigned int dq_result = ddr_read(data->base_phy + DDR_PHY_PHYDQRESULT);
|
|
unsigned int pattern_p = ddr_read(data->base_phy
|
|
+ DDR_PHY_SWCATPATTERN_P) & PHY_CAT_PATTERN_MASK;
|
|
unsigned int pattern_n = ddr_read(data->base_phy
|
|
+ DDR_PHY_SWCATPATTERN_N) & PHY_CAT_PATTERN_MASK;
|
|
unsigned int index;
|
|
|
|
for (index = 0; index < DDR_PHY_CA_MAX; index++) {
|
|
if (is_ca49) {
|
|
if (4 != index && 9 != index)
|
|
continue;
|
|
} else {
|
|
if (4 == index || 9 == index)
|
|
continue;
|
|
}
|
|
|
|
/* compare result and pattern */
|
|
if (!ddr_lpca_compare(&data->bits[index],
|
|
dq_result, pattern_p, pattern_n, index)) {
|
|
/* pass */
|
|
if (-1 == data->left[index]) {
|
|
data->left[index] = bdl;
|
|
/* set min left bound */
|
|
if (bdl < data->min)
|
|
data->min = bdl;
|
|
}
|
|
|
|
/* unstable border value or abnormal value */
|
|
if ((-1 != data->right[index])
|
|
&& ((bdl - data->right[index]) > 1))
|
|
DDR_WARNING("CA[%x] bdl[%x] right[%x] ph[%x]",
|
|
index, bdl, data->right[index],
|
|
ddr_read(data->base_phy
|
|
+ DDR_PHY_ADDRPHBOUND));
|
|
|
|
data->right[index] = bdl;
|
|
data->done |= (0x1 << index);
|
|
|
|
/* set max right bound */
|
|
if (data->right[index] > data->max)
|
|
data->max = data->right[index];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Excute lpca command and check result */
|
|
static void ddr_lpca_excute(struct ca_data_st *data, unsigned int bdl,
|
|
unsigned int is_ca49)
|
|
{
|
|
volatile union U_PHY_CATCONFIG *ca = (union U_PHY_CATCONFIG *)
|
|
(data->base_phy + DDR_PHY_CATCONFIG);
|
|
|
|
if (is_ca49)
|
|
ca->bits.sw_cat_mrw48 = 1;
|
|
else
|
|
ca->bits.sw_cat_mrw41 = 1;
|
|
|
|
ddr_lpca_wait(ca);
|
|
ca->bits.sw_cat_cke_low = 1;
|
|
ddr_lpca_wait(ca);
|
|
ca->bits.sw_cat_strobe = 1;
|
|
ddr_lpca_wait(ca);
|
|
|
|
/* check PHYDQRESULT */
|
|
ddr_lpca_check(data, bdl, is_ca49);
|
|
|
|
ca->bits.sw_cat_cke_high = 1;
|
|
ddr_lpca_wait(ca);
|
|
ca->bits.sw_cat_mrw42 = 1;
|
|
ddr_lpca_wait(ca);
|
|
}
|
|
|
|
/* Find address bdl */
|
|
static int ddr_lpca_find_bdl(struct ca_data_st *data)
|
|
{
|
|
unsigned int bdl;
|
|
|
|
for (bdl = 0; bdl <= PHY_ACADDR_BDL_MASK; bdl++) {
|
|
/* update bdl */
|
|
ddr_lpca_set_bdl(data->base_phy, bdl);
|
|
|
|
/* ca0~ca3, ca5~ca8 */
|
|
ddr_lpca_excute(data, bdl, DDR_FALSE);
|
|
|
|
/* ca4, ca9 */
|
|
ddr_lpca_excute(data, bdl, DDR_TRUE);
|
|
}
|
|
|
|
if (PHY_CAT_PATTERN_MASK == data->done)
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Loop phase to find valid bdl and phase */
|
|
static int ddr_lpca_loop_phase(struct ca_data_st *data, int step)
|
|
{
|
|
volatile union U_PHY_ADDRPHBOUND *ph = (union U_PHY_ADDRPHBOUND *)
|
|
(data->base_phy + DDR_PHY_ADDRPHBOUND);
|
|
unsigned int phase;
|
|
unsigned int addrph_def = ph->bits.addrph_a;
|
|
int addrph = addrph_def;
|
|
|
|
for (phase = 0; phase <= PHY_ADDRPH_MASK; phase++) {
|
|
/* reset ca training data */
|
|
ddr_lpca_reset(data);
|
|
|
|
/* find bdl */
|
|
if (!ddr_lpca_find_bdl(data))
|
|
return 0;
|
|
|
|
addrph += step;
|
|
if (addrph < 0 || addrph > PHY_ADDRPH_MASK)
|
|
break;
|
|
|
|
ph->bits.addrph_a = addrph;
|
|
ddr_phy_cfg_update(data->base_phy);
|
|
}
|
|
|
|
/* restore default value */
|
|
DDR_DEBUG("current phase[%x = %x], restore default[%x]",
|
|
ph, *ph, addrph_def);
|
|
ph->bits.addrph_a = addrph_def;
|
|
return -1;
|
|
}
|
|
|
|
/* Find a valid phase */
|
|
static int ddr_lpca_find_phase(struct ca_data_st *data)
|
|
{
|
|
/* increase default value to find */
|
|
if (!ddr_lpca_loop_phase(data, 1))
|
|
return 0;
|
|
|
|
/* decrease default value to find */
|
|
if (!ddr_lpca_loop_phase(data, -1))
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Set step to adjust address window */
|
|
static int ddr_lpca_set_step(struct ca_data_st *data)
|
|
{
|
|
/* max window, no need to found */
|
|
if (0 == data->min && PHY_ACADDR_BDL_MASK == data->max)
|
|
return 0;
|
|
|
|
if (0 == data->min)
|
|
return -1; /* window on left, move to right */
|
|
else
|
|
return 1; /* window on right, move to left */
|
|
}
|
|
|
|
/**
|
|
* Adjust address window via change phase.
|
|
* Increase phase, window will move to left.
|
|
*/
|
|
static void ddr_lpca_adjust(struct ca_data_st *data)
|
|
{
|
|
int step = 0;
|
|
volatile union U_PHY_ADDRPHBOUND *ph = (union U_PHY_ADDRPHBOUND *)
|
|
(data->base_phy + DDR_PHY_ADDRPHBOUND);
|
|
unsigned int phase;
|
|
unsigned int addrph_last = ph->bits.addrph_a;
|
|
int addrph_cur = addrph_last;
|
|
|
|
/* set step to increase or decrease phase */
|
|
step = ddr_lpca_set_step(data);
|
|
|
|
if (!step)
|
|
return;
|
|
|
|
for (phase = 0; phase <= PHY_ADDRPH_MASK; phase++) {
|
|
addrph_cur += step;
|
|
if (addrph_cur < 0 || addrph_cur > PHY_ADDRPH_MASK)
|
|
return;
|
|
|
|
ph->bits.addrph_a = addrph_cur;
|
|
ddr_phy_cfg_update(data->base_phy);
|
|
|
|
/* reset ca training data */
|
|
ddr_lpca_reset(data);
|
|
|
|
if (ddr_lpca_find_bdl(data)) {
|
|
/* not find bdl, restore last value */
|
|
addrph_cur -= step;
|
|
ddr_lpca_find_bdl(data);
|
|
return;
|
|
}
|
|
|
|
/* max window: ------- */
|
|
if (0 == data->min && PHY_ACADDR_BDL_MASK == data->max)
|
|
return;
|
|
|
|
/* last window: -----xx */
|
|
if (0 == data->min && 1 == step) {
|
|
/* last value is best */
|
|
addrph_cur -= step;
|
|
ph->bits.addrph_a = addrph_cur;
|
|
ddr_phy_cfg_update(data->base_phy);
|
|
ddr_lpca_reset(data);
|
|
ddr_lpca_find_bdl(data);
|
|
return;
|
|
}
|
|
|
|
/* best window: x-----x */
|
|
if (0 < data->min && -1 == step)
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Low power DDR CA training */
|
|
int ddr_lpca_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
volatile union U_PHY_CATCONFIG *ca = (union U_PHY_CATCONFIG *)
|
|
(cfg->cur_phy + DDR_PHY_CATCONFIG);
|
|
|
|
struct ca_data_st data;
|
|
int ret = -1;
|
|
|
|
DDR_DEBUG("DDR LPCA training.");
|
|
|
|
/* init data */
|
|
ddr_lpca_init(cfg->cur_dmc, cfg->cur_phy, &data);
|
|
|
|
/* enable sw ca training, wait 62.5ns */
|
|
ca->bits.sw_cat_en = 1;
|
|
|
|
/* find a valid phase first */
|
|
ret = ddr_lpca_find_phase(&data);
|
|
|
|
/* display training result */
|
|
ddr_lpca_display(&data);
|
|
|
|
if (ret) {
|
|
/* restore default value when fail */
|
|
ddr_lpca_restore_def(&data);
|
|
DDR_ERROR("PHY[%x] found phase fail, result[%x].",
|
|
cfg->cur_phy, data.done);
|
|
ddr_training_stat(DDR_ERR_LPCA, cfg->cur_phy, -1, -1);
|
|
} else {
|
|
/* adjust window via phase */
|
|
ddr_lpca_adjust(&data);
|
|
ddr_lpca_display(&data);
|
|
/* set training result */
|
|
ddr_lpca_update_bdl(&data);
|
|
}
|
|
|
|
/* disable sw ca training */
|
|
ca->bits.sw_cat_en = 0;
|
|
|
|
/* save lpca result data to printf */
|
|
ddr_lpca_data_save(&data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ddr_lpca_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
int result = 0;
|
|
struct tr_relate_reg relate_reg;
|
|
|
|
/* LPCA training disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_LPCA_MASK))
|
|
return 0;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_LPCA_MASK);
|
|
|
|
/* only lowpower ddr3 support */
|
|
if (PHY_DRAMCFG_TYPE_LPDDR3 ==
|
|
(ddr_read(cfg->cur_phy + DDR_PHY_DRAMCFG)
|
|
& PHY_DRAMCFG_TYPE_LPDDR3))
|
|
result += ddr_lpca_training(cfg);
|
|
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_lpca_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support LPDDR CA training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_LPCA_TRAINING_CONFIG */
|
|
|
|
/* s40/t28/t16 not support dcc training */
|
|
#define __dcc_training__
|
|
#ifdef DDR_DCC_TRAINING_CONFIG
|
|
/* Save two rank RDET result */
|
|
static void ddr_save_two_rank_bdl(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data)
|
|
{
|
|
unsigned int byte_idx;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int rank_idx = cfg->rank_idx;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
|
|
for (byte_idx = 0; byte_idx < byte_num; byte_idx++) {
|
|
dcc_data->rank[rank_idx].dq03[byte_idx] = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL0(rank_idx, byte_idx));
|
|
dcc_data->rank[rank_idx].dq47[byte_idx] = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL1(rank_idx, byte_idx));
|
|
dcc_data->rank[rank_idx].rdm[byte_idx] = ddr_read(base_phy + DDR_PHY_DXNRDQNBDL2(rank_idx, byte_idx));
|
|
dcc_data->rank[rank_idx].rdqs[byte_idx] = ddr_read(base_phy + DDR_PHY_DXNRDQSDLY(byte_idx));
|
|
|
|
DDR_DEBUG("rank[%x] dq03[%x] dq47[%x] rdm[%x] rdqs[%x]", rank_idx,
|
|
dcc_data->rank[rank_idx].dq03[byte_idx],
|
|
dcc_data->rank[rank_idx].dq47[byte_idx],
|
|
dcc_data->rank[rank_idx].rdm[byte_idx],
|
|
dcc_data->rank[rank_idx].rdqs[byte_idx]);
|
|
}
|
|
}
|
|
|
|
/* Restore two rank RDET result */
|
|
static void ddr_restore_two_rank_bdl(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data)
|
|
{
|
|
unsigned int byte_idx;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int rank_idx = cfg->rank_idx;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
|
|
for (byte_idx = 0; byte_idx < byte_num; byte_idx++) {
|
|
ddr_write(dcc_data->rank[rank_idx].dq03[byte_idx], base_phy + DDR_PHY_DXNRDQNBDL0(rank_idx, byte_idx));
|
|
ddr_write(dcc_data->rank[rank_idx].dq47[byte_idx], base_phy + DDR_PHY_DXNRDQNBDL1(rank_idx, byte_idx));
|
|
ddr_write(dcc_data->rank[rank_idx].rdm[byte_idx], base_phy + DDR_PHY_DXNRDQNBDL2(rank_idx, byte_idx));
|
|
ddr_write(dcc_data->rank[rank_idx].rdqs[byte_idx], base_phy + DDR_PHY_DXNRDQSDLY(byte_idx));
|
|
}
|
|
}
|
|
|
|
/* DMC_CFG_SREF exit self-refresa enter powerdown */
|
|
static void ddr_exit_sref_enter_pd(struct ddr_cfg_st *cfg, struct dmc_cfg_sref_st *cfg_sref)
|
|
{
|
|
int i;
|
|
struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx];
|
|
|
|
for (i = 0; i < phy_st->dmc_num; i++) {
|
|
cfg_sref->val[i] = ddr_read(phy_st->dmc[i].addr + DDR_DMC_CFG_SREF);
|
|
ddr_write((cfg_sref->val[i] & (~DMC_CFG_INIT_XSREF_PD_MASK)) | DMC_CFG_INIT_XSREF_PD,
|
|
phy_st->dmc[i].addr + DDR_DMC_CFG_SREF);
|
|
}
|
|
}
|
|
|
|
/* Restore DMC_CFG_SREF config */
|
|
static void ddr_restore_sref_cfg(struct ddr_cfg_st *cfg, struct dmc_cfg_sref_st *cfg_sref)
|
|
{
|
|
int i;
|
|
struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx];
|
|
|
|
for (i = 0; i < phy_st->dmc_num; i++) {
|
|
ddr_write(cfg_sref->val[i], phy_st->dmc[i].addr + DDR_DMC_CFG_SREF);
|
|
}
|
|
}
|
|
|
|
/* DCC RDET training */
|
|
static int ddr_dcc_dataeye_read(struct ddr_cfg_st *cfg)
|
|
{
|
|
/* 0:PHY_TRAINCTRL0_DTR_RANK0, 1:PHY_TRAINCTRL0_DTR_RANK1 */
|
|
DDR_PHY_SWITCH_RANK(cfg->cur_phy, cfg->rank_idx);
|
|
return ddr_hw_training_process(cfg, PHY_PHYINITCTRL_RDET_EN);
|
|
}
|
|
|
|
/* Duty direction ctl */
|
|
static int ddr_dcc_ck_ctl(struct ddr_cfg_st *cfg, unsigned int ioctl21_def, unsigned int ctl_index)
|
|
{
|
|
unsigned int ioctl21;
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
ioctl21 = (ioctl21_def & (~(1 << PHY_ACIOCTL21_CTL0_BIT))
|
|
& (~(1 << PHY_ACIOCTL21_CTL1_BIT)))
|
|
| (ctl_index << PHY_ACIOCTL21_CTL0_BIT)
|
|
| (ctl_index << PHY_ACIOCTL21_CTL1_BIT);
|
|
ddr_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21);
|
|
} else {
|
|
ioctl21 = (ioctl21_def & (~(1 << PHY_ACIOCTL21_CTL0_BIT)))
|
|
| (ctl_index << PHY_ACIOCTL21_CTL0_BIT);
|
|
ddr_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21);
|
|
}
|
|
return ioctl21;
|
|
}
|
|
|
|
/* Duty Correction */
|
|
static int ddr_dcc_correct_duty(struct ddr_cfg_st *cfg, unsigned int cur_duty, unsigned int duty_def)
|
|
{
|
|
unsigned int ioctl21;
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
/* Correct CK0 & CK1 duty */
|
|
ioctl21 = (duty_def & (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK0_BIT))
|
|
& (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK1_BIT)))
|
|
| (cur_duty << PHY_ACIOCTL21_CK0_BIT)
|
|
| (cur_duty << PHY_ACIOCTL21_CK1_BIT);
|
|
ddr_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21);
|
|
} else {
|
|
/* Correct CK0 duty */
|
|
ioctl21 = (duty_def & (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK0_BIT)))
|
|
| (cur_duty << PHY_ACIOCTL21_CK0_BIT);
|
|
ddr_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21);
|
|
}
|
|
|
|
return ioctl21;
|
|
}
|
|
|
|
/* Duty Correction Control get win data */
|
|
static int ddr_dcc_get_win(struct dcc_data_st *dcc_data, int ck_index, int val_index)
|
|
{
|
|
unsigned int win;
|
|
unsigned int rdqsbdl_right;
|
|
unsigned int rdqsbdl_left;
|
|
rdqsbdl_right = dcc_data->ck[ck_index].val[val_index] >> PHY_DXNRDBOUND_RIGHT_BIT & PHY_DXNRDBOUND_MASK;
|
|
rdqsbdl_left = dcc_data->ck[ck_index].val[val_index] >> PHY_DXNRDBOUND_LEFT_BIT & PHY_DXNRDBOUND_MASK;
|
|
win = rdqsbdl_right - rdqsbdl_left;
|
|
return win;
|
|
}
|
|
|
|
/* Duty Correction Control get the min win of two byte */
|
|
static int ddr_dcc_get_min_win(struct dcc_data_st *dcc_data, int ck_index)
|
|
{
|
|
int i;
|
|
unsigned int win_min;
|
|
unsigned int cur_win;
|
|
win_min = ddr_dcc_get_win(dcc_data, ck_index, 0);
|
|
for (i = 0; i < DDR_CK_RESULT_MAX; i++) {
|
|
cur_win = ddr_dcc_get_win(dcc_data, ck_index, i);
|
|
DDR_DEBUG("CK win[%x] = [%x]", i, cur_win);
|
|
if (cur_win < win_min) {
|
|
win_min = cur_win;
|
|
}
|
|
}
|
|
return win_min;
|
|
}
|
|
|
|
/* Duty Correction Control get ck0 min win */
|
|
static int ddr_dcc_get_ck0_win(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data,
|
|
int rank_index, unsigned int ck0_win_min)
|
|
{
|
|
int ck_index = 0;
|
|
unsigned int byte_index;
|
|
unsigned int ck0_win;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
|
|
for (byte_index = 0; byte_index < (byte_num/2); byte_index++) {
|
|
dcc_data->ck[ck_index].val[byte_index] = ddr_read(cfg->cur_phy + DDR_PHY_DXNRDBOUND(byte_index));
|
|
}
|
|
|
|
ck0_win = ddr_dcc_get_min_win(dcc_data, ck_index);
|
|
|
|
if (ck0_win < ck0_win_min)
|
|
ck0_win_min = ck0_win;
|
|
|
|
return ck0_win_min;
|
|
}
|
|
|
|
/* Duty Correction Control get ck1 min win */
|
|
static int ddr_dcc_get_ck1_win(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data,
|
|
int rank_index, unsigned int ck1_win_min)
|
|
{
|
|
int ck_index = 1;
|
|
unsigned int byte_index;
|
|
unsigned int ck1_win;
|
|
unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num;
|
|
|
|
for (byte_index = 2; byte_index < byte_num; byte_index++) {
|
|
dcc_data->ck[ck_index].val[byte_index - 2] = ddr_read(cfg->cur_phy + DDR_PHY_DXNRDBOUND(byte_index));
|
|
}
|
|
|
|
ck1_win = ddr_dcc_get_min_win(dcc_data, ck_index);
|
|
|
|
if (ck1_win < ck1_win_min)
|
|
ck1_win_min = ck1_win;
|
|
|
|
return ck1_win_min;
|
|
}
|
|
|
|
static void dcc_data_init(struct dcc_data_st *dcc_data)
|
|
{
|
|
dcc_data->ck[0].win_min_ctl = 0xffffffff;
|
|
dcc_data->ck[0].win_max_ctl = 0x0;
|
|
dcc_data->ck[1].win_min_ctl = 0xffffffff;
|
|
dcc_data->ck[1].win_max_ctl = 0x0;
|
|
dcc_data->ck[0].idx_duty = 0;
|
|
dcc_data->ck[0].idx_duty_ctl = 0;
|
|
dcc_data->ck[0].idx_ctl = 0;
|
|
dcc_data->ck[1].idx_duty = 0;
|
|
dcc_data->ck[1].idx_duty_ctl = 0;
|
|
dcc_data->ck[1].idx_ctl = 0;
|
|
dcc_data->ck[0].BYPASS_CK_BIT = PHY_BYPASS_CK0_BIT;
|
|
dcc_data->ck[0].ACIOCTL21_CTL_BIT = PHY_ACIOCTL21_CTL0_BIT;
|
|
dcc_data->ck[0].ACIOCTL21_CK_BIT = PHY_ACIOCTL21_CK0_BIT;
|
|
dcc_data->ck[1].BYPASS_CK_BIT = PHY_BYPASS_CK1_BIT;
|
|
dcc_data->ck[1].ACIOCTL21_CTL_BIT = PHY_ACIOCTL21_CTL1_BIT;
|
|
dcc_data->ck[1].ACIOCTL21_CK_BIT = PHY_ACIOCTL21_CK1_BIT;
|
|
}
|
|
|
|
/* dcc training get window by rank */
|
|
static int ddr_dcc_get_win_by_rank(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data)
|
|
{
|
|
int i;
|
|
int result = 0;
|
|
unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num;
|
|
for (i = 0; i < rank_num; i++) {
|
|
DDR_DEBUG("cur_rank = [%x]", i);
|
|
cfg->rank_idx = i;
|
|
/* RDET */
|
|
result += ddr_dcc_dataeye_read(cfg);
|
|
|
|
/* Get win */
|
|
dcc_data->ck[0].win = ddr_dcc_get_ck0_win(cfg, dcc_data, i, dcc_data->ck[0].win);
|
|
DDR_DEBUG("ck0 win = [%x]", dcc_data->ck[0].win);
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
dcc_data->ck[1].win = ddr_dcc_get_ck1_win(cfg, dcc_data, i, dcc_data->ck[1].win);
|
|
DDR_DEBUG("ck1 win = [%x]", dcc_data->ck[1].win);
|
|
}
|
|
|
|
/* Restore two rank bdl */
|
|
ddr_restore_two_rank_bdl(cfg, dcc_data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* ddr dcc training compare result */
|
|
static void ddr_dcc_compare_result(struct dcc_data_st *dcc_data, int ck_num,
|
|
unsigned int base_phy, unsigned int gated_bypass_def, unsigned int ioctl21_def)
|
|
{
|
|
int ck_idx;
|
|
|
|
for (ck_idx = 0; ck_idx < ck_num; ck_idx++) {
|
|
/* Config ck0 duty */
|
|
if (dcc_data->ck[ck_idx].win_max_ctl - dcc_data->ck[ck_idx].win_min_ctl <= 2) {
|
|
dcc_data->ck[ck_idx].def_bp = gated_bypass_def >> dcc_data->ck[ck_idx].BYPASS_CK_BIT & 0x1;
|
|
dcc_data->ck[ck_idx].def_ctl = ioctl21_def >> dcc_data->ck[ck_idx].ACIOCTL21_CTL_BIT & 0x1;
|
|
dcc_data->ck[ck_idx].def_duty = ioctl21_def >> dcc_data->ck[ck_idx].ACIOCTL21_CK_BIT & PHY_ACIOCTL21_MASK;
|
|
|
|
gated_bypass_def = (gated_bypass_def & (~(1 << dcc_data->ck[ck_idx].BYPASS_CK_BIT)))
|
|
| (dcc_data->ck[ck_idx].def_bp << dcc_data->ck[ck_idx].BYPASS_CK_BIT);
|
|
ddr_write(gated_bypass_def, base_phy + DDR_PHY_AC_GATED_BYPASS);
|
|
|
|
ioctl21_def = (ioctl21_def & (~(1 << dcc_data->ck[ck_idx].ACIOCTL21_CTL_BIT))
|
|
& (~(PHY_ACIOCTL21_MASK << dcc_data->ck[ck_idx].ACIOCTL21_CK_BIT)))
|
|
| (dcc_data->ck[ck_idx].def_ctl << dcc_data->ck[ck_idx].ACIOCTL21_CTL_BIT)
|
|
| (dcc_data->ck[ck_idx].def_duty << dcc_data->ck[ck_idx].ACIOCTL21_CK_BIT);
|
|
ddr_write(ioctl21_def, base_phy + DDR_PHY_ACIOCTL21);
|
|
|
|
DDR_DEBUG("ck[%x] Final AC_GATED_BYPASS[%x]", ck_idx, gated_bypass_def);
|
|
DDR_DEBUG("ck[%x] Final ACIOCTL21[%x]", ck_idx, ioctl21_def);
|
|
} else {
|
|
ioctl21_def = (ioctl21_def & (~(1 << dcc_data->ck[ck_idx].ACIOCTL21_CTL_BIT))
|
|
& (~(PHY_ACIOCTL21_MASK << dcc_data->ck[ck_idx].ACIOCTL21_CK_BIT)))
|
|
| (dcc_data->ck[ck_idx].idx_ctl << dcc_data->ck[ck_idx].ACIOCTL21_CTL_BIT)
|
|
| (dcc_data->ck[ck_idx].idx_duty_ctl << dcc_data->ck[ck_idx].ACIOCTL21_CK_BIT);
|
|
ddr_write(ioctl21_def, base_phy + DDR_PHY_ACIOCTL21);
|
|
|
|
DDR_DEBUG("ck[%x] Final ACIOCTL21[%x]", ck_idx, ioctl21_def);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ddr_dcc_get_best_duty(struct ddr_cfg_st *cfg,
|
|
struct dmc_cfg_sref_st *cfg_sref, struct dcc_data_st *dcc_data)
|
|
{
|
|
int ck_idx;
|
|
int ck_num;
|
|
int result = 0;
|
|
unsigned int cur_ctl;
|
|
unsigned int cur_duty;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int ioctl21_def;
|
|
unsigned int gated_bypass_def, gated_bypass_temp;
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type)
|
|
ck_num = 2;
|
|
else
|
|
ck_num = 1;
|
|
|
|
dcc_data_init(dcc_data);
|
|
|
|
/* Save ck duty default config. Read two times to get the right static register value. */
|
|
gated_bypass_def = ddr_read(base_phy + DDR_PHY_AC_GATED_BYPASS);
|
|
gated_bypass_def = ddr_read(base_phy + DDR_PHY_AC_GATED_BYPASS);
|
|
ioctl21_def = ddr_read(base_phy + DDR_PHY_ACIOCTL21);
|
|
ioctl21_def = ddr_read(base_phy + DDR_PHY_ACIOCTL21);
|
|
|
|
DDR_DEBUG("gated_bypass_def[%x] ioctl21_def[%x]", gated_bypass_def, ioctl21_def);
|
|
|
|
/* DCC training exit self-refresa enter powerdown. */
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type)
|
|
ddr_exit_sref_enter_pd(cfg, cfg_sref);
|
|
|
|
/* DDR dcc training enter auto self-refresh. */
|
|
if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF))
|
|
return -1;
|
|
|
|
/* Enable ck0 & ck1 duty. */
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
gated_bypass_temp = gated_bypass_def | PHY_CK1_IOCTL_DUTY_EN | PHY_CK_IOCTL_DUTY_EN;
|
|
ddr_write(gated_bypass_temp, base_phy + DDR_PHY_AC_GATED_BYPASS);
|
|
} else {
|
|
gated_bypass_temp = gated_bypass_def | PHY_CK_IOCTL_DUTY_EN;
|
|
ddr_write(gated_bypass_temp, base_phy + DDR_PHY_AC_GATED_BYPASS);
|
|
}
|
|
DDR_DEBUG("Cur GATED_BYPASS[%x]", gated_bypass_temp);
|
|
|
|
if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF))
|
|
return -1;
|
|
|
|
for (cur_ctl = 0; cur_ctl < DDR_DUTY_CTL_NUM; cur_ctl++) {
|
|
dcc_data->ck[0].win_min_duty = 0xffffffff;
|
|
dcc_data->ck[0].win_max_duty = 0x0;
|
|
dcc_data->ck[1].win_min_duty = 0xffffffff;
|
|
dcc_data->ck[1].win_max_duty = 0x0;
|
|
|
|
DDR_DEBUG("cur_ctl = [%x]", cur_ctl);
|
|
|
|
if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF))
|
|
return -1;
|
|
|
|
/* Correct CK duty dirrection control */
|
|
dcc_data->ioctl21_tmp = ddr_dcc_ck_ctl(cfg, ioctl21_def, cur_ctl);
|
|
|
|
if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF))
|
|
return -1;
|
|
|
|
for (cur_duty = 0; cur_duty < DDR_DUTY_NUM; cur_duty += PHY_AC_IOCTL21_STEP) {
|
|
dcc_data->ck[0].win = 0xffffffff;
|
|
dcc_data->ck[1].win = 0xffffffff;
|
|
|
|
DDR_DEBUG("cur_duty = [%x]", cur_duty);
|
|
/* Correct ck0 and ck1 duty */
|
|
if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF))
|
|
return -1;
|
|
dcc_data->ioctl21_tmp = ddr_dcc_correct_duty(cfg, cur_duty, dcc_data->ioctl21_tmp);
|
|
if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF))
|
|
return -1;
|
|
DDR_DEBUG("Cur ACIOCTL21[%x]", dcc_data->ioctl21_tmp);
|
|
|
|
result = ddr_dcc_get_win_by_rank(cfg, dcc_data);
|
|
|
|
/* Get ck0/ck1 duty_win_min/duty_win_max/duty_index */
|
|
for (ck_idx = 0; ck_idx < ck_num; ck_idx++) {
|
|
if (dcc_data->ck[ck_idx].win < dcc_data->ck[ck_idx].win_min_duty)
|
|
dcc_data->ck[ck_idx].win_min_duty = dcc_data->ck[ck_idx].win;
|
|
|
|
if (dcc_data->ck[ck_idx].win > dcc_data->ck[ck_idx].win_max_duty) {
|
|
dcc_data->ck[ck_idx].win_max_duty = dcc_data->ck[ck_idx].win;
|
|
dcc_data->ck[ck_idx].idx_duty = cur_duty;
|
|
}
|
|
DDR_DEBUG("ck[%x] duty_win_min[%x] duty_win_max[%x] duty_index[%x]", ck_idx,
|
|
dcc_data->ck[ck_idx].win_min_duty,
|
|
dcc_data->ck[ck_idx].win_max_duty,
|
|
dcc_data->ck[ck_idx].idx_duty);
|
|
}
|
|
}
|
|
|
|
for (ck_idx = 0; ck_idx < ck_num; ck_idx++) {
|
|
/* Get ck0/ck1 duty_win_min/duty_win_max/duty_index */
|
|
if (dcc_data->ck[ck_idx].win_min_duty < dcc_data->ck[ck_idx].win_min_ctl)
|
|
dcc_data->ck[ck_idx].win_min_ctl = dcc_data->ck[ck_idx].win_min_duty;
|
|
|
|
if (dcc_data->ck[ck_idx].win_max_duty > dcc_data->ck[ck_idx].win_max_ctl) {
|
|
dcc_data->ck[ck_idx].win_max_ctl = dcc_data->ck[ck_idx].win_max_duty;
|
|
dcc_data->ck[ck_idx].idx_duty_ctl = dcc_data->ck[ck_idx].idx_duty;
|
|
dcc_data->ck[ck_idx].idx_ctl = cur_ctl;
|
|
}
|
|
DDR_DEBUG("ck[%x] win_min_ctl[%x] win_max_ctl[%x] ctl_index0[%x] duty_ctl_idx0[%x]", ck_idx,
|
|
dcc_data->ck[ck_idx].win_min_ctl,
|
|
dcc_data->ck[ck_idx].win_max_ctl,
|
|
dcc_data->ck[ck_idx].idx_ctl,
|
|
dcc_data->ck[ck_idx].idx_duty_ctl);
|
|
}
|
|
}
|
|
|
|
/* Config ck duty */
|
|
/* DCC training exit self-refresa enter powerdown. */
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type)
|
|
ddr_exit_sref_enter_pd(cfg, cfg_sref);
|
|
|
|
/* DDR dcc training enter auto self-refresh. */
|
|
if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF))
|
|
return -1;
|
|
|
|
/* DDR dcc training compare result. */
|
|
ddr_dcc_compare_result(dcc_data, ck_num, base_phy, gated_bypass_def, ioctl21_def);
|
|
|
|
/* DDR dcc training exit auto self-refresh. */
|
|
if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF))
|
|
return -1;
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef DDR_TRAINING_DEBUG
|
|
#define DDR_TRINING_BREAK_POINT(name) ddr_training_break_point(name)
|
|
#else
|
|
#define DDR_TRINING_BREAK_POINT(name)
|
|
#endif
|
|
|
|
void ddr_training_break_point(const char* name)
|
|
{
|
|
DDR_INFO(name);
|
|
ddr_training_console_if(0);
|
|
}
|
|
|
|
int ddr_dcc_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
int i;
|
|
int result = 0;
|
|
unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num;
|
|
|
|
struct dmc_cfg_sref_st cfg_sref;
|
|
struct ddr_timing_st timing_st;
|
|
struct dcc_data_st dcc_st;
|
|
struct dcc_data_st *dcc_data = &dcc_st;
|
|
|
|
DDR_DEBUG("dram_type[%x]", cfg->phy[cfg->phy_idx].dram_type);
|
|
|
|
DDR_DEBUG("rank num[%x]", rank_num);
|
|
|
|
/* Save two rank DERT default result: rdq/rdqs/rdm/ bdl */
|
|
for (i = 0; i < rank_num; i++) {
|
|
cfg->rank_idx = i;
|
|
ddr_save_two_rank_bdl(cfg, dcc_data);
|
|
}
|
|
|
|
/* Disable auto refresh */
|
|
ddr_training_save_timing(cfg, &timing_st);
|
|
|
|
/* Duty Correction Control training. */
|
|
result += ddr_dcc_get_best_duty(cfg, &cfg_sref, dcc_data);
|
|
|
|
/* Do DERT training again */
|
|
for (i = 0; i < rank_num; i++) {
|
|
cfg->rank_idx = i;
|
|
dcc_data->item[i] = cfg->phy[cfg->phy_idx].rank[i].item_hw;
|
|
cfg->phy[cfg->phy_idx].rank[i].item_hw = PHY_PHYINITCTRL_HVREFT_EN;
|
|
DDR_DEBUG("item_hw[%x]=[%x]", i, cfg->phy[cfg->phy_idx].rank[i].item_hw);
|
|
}
|
|
|
|
result += ddr_hw_training_by_phy(cfg);
|
|
|
|
for (i = 0; i < rank_num; i++) {
|
|
cfg->rank_idx = i;
|
|
cfg->phy[cfg->phy_idx].rank[i].item_hw = dcc_data->item[i];
|
|
}
|
|
|
|
/* Enable auto refresh */
|
|
ddr_training_restore_timing(cfg, &timing_st);
|
|
|
|
if (PHY_DRAMCFG_TYPE_LPDDR4 == cfg->phy[cfg->phy_idx].dram_type) {
|
|
/* DCC restore DMC_CFG_SREF config. */
|
|
ddr_restore_sref_cfg(cfg, &cfg_sref);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int ddr_dcc_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
int i;
|
|
int result = 0;
|
|
|
|
for (i = 0; i < cfg->phy_num; i++) {
|
|
cfg->phy_idx = i;
|
|
cfg->cur_phy = cfg->phy[i].addr;
|
|
cfg->cur_item = cfg->phy[i].rank[0].item;
|
|
|
|
if (ddr_training_check_bypass(cfg, 1 << (cfg->phy_idx)))
|
|
continue;
|
|
|
|
/* dpmc training disable */
|
|
if (!ddr_training_check_bypass(cfg, DDR_BYPASS_DCC_MASK))
|
|
result += ddr_dcc_training(cfg);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#else
|
|
int ddr_dcc_training_func(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DCC training.");
|
|
return 0;
|
|
}
|
|
#endif /* DDR_DCC_TRAINING_CONFIG */
|
|
|
|
#define __pcode_training__
|
|
#ifdef DDR_PCODE_TRAINING_CONFIG
|
|
/* Set pcode value to register IMPSTATUS and DDR_PHY_IMP_STATUS1 */
|
|
static void ddr_pcode_set_value(unsigned int base_phy, unsigned int pcode_value)
|
|
{
|
|
unsigned int imp_ctrl1;
|
|
|
|
ddr_write((ddr_read(base_phy + DDR_PHY_IMPSTATUS)
|
|
& (~(PHY_ZCODE_PDRV_MASK << PHY_ZCODE_PDRV_BIT)))
|
|
| (pcode_value << PHY_ZCODE_PDRV_BIT), base_phy + DDR_PHY_IMPSTATUS);
|
|
DDR_DEBUG("cur IMPSTATUS [%x] = [%x]",
|
|
base_phy + DDR_PHY_IMPSTATUS, ddr_read(base_phy + DDR_PHY_IMPSTATUS));
|
|
|
|
imp_ctrl1 = ddr_read(base_phy + DDR_PHY_IMP_CTRL1);
|
|
/* ac_vddq_cal_en set 0 */
|
|
ddr_write(imp_ctrl1 & (~(0x1 << PHY_AC_VDDQ_CAL_EN_BIT)), base_phy + DDR_PHY_IMP_CTRL1);
|
|
|
|
ddr_write((ddr_read(base_phy + DDR_PHY_IMP_STATUS1)
|
|
& (~(PHY_ACCTL_PDRV_LATCH_MASK << PHY_ACCTL_PDRV_LATCH_BIT)))
|
|
| (pcode_value << PHY_ACCTL_PDRV_LATCH_BIT), base_phy + DDR_PHY_IMP_STATUS1);
|
|
DDR_DEBUG("cur IMP_STATUS1 [%x] = [%x]",
|
|
base_phy + DDR_PHY_IMP_STATUS1, ddr_read(base_phy + DDR_PHY_IMP_STATUS1));
|
|
|
|
/* restore ac_vddq_cal_en */
|
|
ddr_write(imp_ctrl1, base_phy + DDR_PHY_IMP_CTRL1);
|
|
}
|
|
|
|
static int ddr_pcode_trainig_by_phy(struct ddr_cfg_st *cfg)
|
|
{
|
|
unsigned int times = 0;
|
|
unsigned int base_phy = cfg->cur_phy;
|
|
unsigned int pcode_value;
|
|
unsigned int osc_rpt_vld;
|
|
unsigned int osc_cnt_rdata;
|
|
int ddr_freq;
|
|
|
|
/* test start */
|
|
ddr_write(ddr_read(base_phy + DDR_PHY_CORNER_DETECTOR) | PHY_OSC_START_MASK,
|
|
base_phy + DDR_PHY_CORNER_DETECTOR);
|
|
|
|
do {
|
|
osc_rpt_vld = (ddr_read(base_phy + DDR_PHY_CORNER_DETECTOR)
|
|
>> PHY_OSC_RPT_VLD) & PHY_OSC_RPT_VLD_MASK;
|
|
times++;
|
|
} while ((!osc_rpt_vld)
|
|
&& (times < DDRT_PCODE_WAIT_TIMEOUT));
|
|
|
|
if (times >= DDRT_PCODE_WAIT_TIMEOUT) {
|
|
DDR_FATAL("IO pcode training wait timeout.");
|
|
return -1;
|
|
}
|
|
|
|
osc_cnt_rdata = (ddr_read(base_phy + DDR_PHY_CORNER_DETECTOR)
|
|
>> PHY_OSC_CNT_RDATA_BIT)
|
|
& PHY_OSC_CNT_RDATA_MASK;
|
|
|
|
/* test stop */
|
|
ddr_write(ddr_read(base_phy + DDR_PHY_CORNER_DETECTOR)
|
|
& (~PHY_OSC_START_MASK), base_phy + DDR_PHY_CORNER_DETECTOR);
|
|
|
|
ddr_freq = ddr_get_cksel();
|
|
/* get pcode value */
|
|
pcode_value = (490960 - (89 * osc_cnt_rdata * ddr_freq) / 300) / 10000;
|
|
|
|
DDR_DEBUG("pcode value[%x]", pcode_value);
|
|
if (pcode_value < PHY_PCODE_MIN) {
|
|
pcode_value = PHY_PCODE_MIN;
|
|
} else if (pcode_value > PHY_PCODE_MAX) {
|
|
pcode_value = PHY_PCODE_MAX;
|
|
}
|
|
|
|
/* set pcode value */
|
|
ddr_pcode_set_value(base_phy, pcode_value);
|
|
return 0;
|
|
}
|
|
|
|
int ddr_pcode_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
struct tr_relate_reg relate_reg;
|
|
int result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < cfg->phy_num; i++) {
|
|
cfg->phy_idx = i;
|
|
cfg->cur_phy = cfg->phy[i].addr;
|
|
cfg->cur_item = cfg->phy[i].rank[0].item;
|
|
|
|
if (ddr_training_check_bypass(cfg, 1 << (cfg->phy_idx)))
|
|
continue;
|
|
|
|
/* pcode training disable */
|
|
if (ddr_training_check_bypass(cfg, DDR_BYPASS_PCODE_MASK))
|
|
continue;
|
|
|
|
ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_PCODE_MASK);
|
|
result += ddr_pcode_trainig_by_phy(cfg);
|
|
ddr_training_restore_reg(cfg, &relate_reg);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
int ddr_pcode_training(struct ddr_cfg_st *cfg)
|
|
{
|
|
DDR_WARNING("Not support DDR pcode training.");
|
|
return 0;
|
|
}
|
|
#endif
|