gk7205v200-uboot/drivers/ddr/goke/default/cmd_bin/ddr_training_cmd.c

491 lines
12 KiB
C
Raw Normal View History

2025-08-07 17:13:54 +08:00
/*
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
*/
#include <stdarg.h>
#include <command.h>
#include "ddr_interface.h"
#include "ddr_training_impl.h"
/* ddr training cmd result */
struct ddr_training_result_st ddrt_result_sram;
static unsigned int ddr_training_addr_start;
static unsigned int ddr_training_addr_end;
static int ddr_print_level = DDR_LOG_ERROR;
#ifdef DDR_TRAINING_LOG_CONFIG
/* Log ddr training info. */
void ddr_training_log(const char *func, int level, const char *fmt, ...)
{
va_list args;
if (ddr_print_level > level)
return;
DDR_PUTC('[');
switch (level) {
case DDR_LOG_INFO:
DDR_PUTS("INFO");
break;
case DDR_LOG_DEBUG:
DDR_PUTS("DEBUG");
break;
case DDR_LOG_WARNING:
DDR_PUTS("WARNING");
break;
case DDR_LOG_ERROR:
DDR_PUTS("ERROR");
break;
case DDR_LOG_FATAL:
DDR_PUTS("FATAL");
break;
default:
break;
}
DDR_PUTC(']');
DDR_PUTC('[');
DDR_PUTS(func);
DDR_PUTC(']');
va_start(args, fmt);
while (*fmt != '\0') {
if (*fmt != '%') {
DDR_PUTC(*fmt);
} else {
fmt++;
switch (*fmt) {
case 'x':
case 'X':
DDR_PUTS("0x");
DDR_PUT_HEX(va_arg(args, int));
break;
default:
DDR_PUTC('%');
DDR_PUTC(*fmt);
break;
} /* switch */
}
fmt++;
} /* while */
va_end(args);
DDR_PUTS("\r\n");
}
/* Nothing to do in DDR command when defined DDR_TRAINING_LOG_CONFIG. */
void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq)
{
return;
}
#else
/* Display DDR training error. */
void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq)
{
switch (mask) {
case DDR_ERR_WL:
DDR_PUTS("WL");
break;
case DDR_ERR_HW_GATING:
DDR_PUTS("HW Gate");
break;
case DDR_ERR_GATING:
DDR_PUTS("Gate");
break;
case DDR_ERR_DDRT_TIME_OUT:
DDR_PUTS("DDRT");
break;
case DDR_ERR_HW_RD_DATAEYE:
DDR_PUTS("HW Dataeye");
break;
case DDR_ERR_MPR:
DDR_PUTS("MPR");
break;
case DDR_ERR_DATAEYE:
DDR_PUTS("Dataeye");
break;
case DDR_ERR_LPCA:
DDR_PUTS("LPCA");
break;
default:
break;
}
DDR_PUTS(" Err:");
if (phy != 0) {
DDR_PUTS(" Phy:");
DDR_PUT_HEX(phy);
}
if (byte != -1) {
DDR_PUTS(" Byte:");
DDR_PUT_HEX(byte);
}
if (dq != -1) {
DDR_PUTS(" DQ:");
DDR_PUT_HEX(dq);
}
DDR_PUTS("\r\n");
}
#endif
/* Inint ddr training cmd result */
static void ddr_training_result_init(struct ddr_cfg_st *cfg, struct ddr_training_result_st *ddrtr_res)
{
int i, j;
ddrtr_memset(ddrtr_res, 0, sizeof(struct ddr_training_result_st));
ddrtr_res->phy_num = cfg->phy_num;
for (i = 0; i < cfg->phy_num; i++) {
ddrtr_res->phy_st[i].rank_num = cfg->phy[i].rank_num;
for (j = 0; j < cfg->phy[i].rank_num; j++) {
ddrtr_res->phy_st[i].rank_st[j].item = cfg->phy[i].rank[j].item;
ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.base_phy = cfg->phy[i].addr;
ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.byte_num = cfg->phy[i].total_byte_num;
ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.rank_idx = j;
}
}
}
/* Save ddr training cmd result */
void ddr_result_data_save(struct ddr_cfg_st *cfg, struct training_data *training)
{
unsigned int i;
unsigned int offset;
struct training_data *dest = NULL;
struct ddr_training_result_st *ddrtr_res = (struct ddr_training_result_st *)cfg->res_st;
if (!ddrtr_res)
return;
if (cfg->cur_mode == DDR_MODE_READ)
dest = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data.read;
else
dest = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data.write;
if (cfg->phy[cfg->phy_idx].dmc_num == 1)
ddrtr_memcpy(dest, training, sizeof(struct training_data));
else {
/* dmc[0] + dmc[1] */
offset = cfg->dmc_idx << 4;
for (i = 0; i < GET_BYTE_NUM(cfg) << 3; i++) {
dest->ddr_bit_result[i + offset] = training->ddr_bit_result[i + offset];
dest->ddr_bit_best[i + offset] = training->ddr_bit_best[i + offset];
}
dest->ddr_win_sum += training->ddr_win_sum;
}
}
/* Save lpca training data */
void ddr_lpca_data_save(struct ca_data_st *data)
{
#if 0
unsigned int index;
struct ddr_training_result_st *result_st
= (struct ddr_training_result_st *)ddrtr_result;
struct ddr_training_data_st *tr_data;
for (index = 0; index < DDR_SUPPORT_PHY_MAX; index++) {
if (result_st->ddrtr_data[index].base_phy == data->base_phy)
break;
}
tr_data = &result_st->ddrtr_data[index];
for (index = 0; index < DDR_PHY_CA_MAX; index++)
tr_data->ca_addr[index] = (data->left[index]
<< DDR_DATAEYE_RESULT_BIT) | data->right[index];
#endif
}
/* Get DDRT test addrress */
unsigned int ddr_ddrt_get_test_addr(void)
{
if (ddr_training_addr_start <= DDRT_CFG_TEST_ADDR_CMD
&& ddr_training_addr_end >= DDRT_CFG_TEST_ADDR_CMD) {
return DDRT_CFG_TEST_ADDR_CMD;
} else {
DDR_ERROR("DDRT test address[%x] out of range[%x, %x]",
DDRT_CFG_TEST_ADDR_CMD,
ddr_training_addr_start,
ddr_training_addr_end);
return ddr_training_addr_start;
}
}
/* Nothing to do in DDR command */
void ddr_training_suc(void) { return; }
/* Nothing to do in DDR command */
void ddr_training_start(void) { return; }
static void dump_result(struct ddr_training_data_st *ddrtr_data)
{
unsigned int i;
unsigned int base_phy = ddrtr_data->base_phy;
unsigned int byte_num = ddrtr_data->byte_num;
unsigned int rank = ddrtr_data->rank_idx;
unsigned int ACPHYCTL7;
/* Static register have to read two times to get the right value. */
ACPHYCTL7 = ddr_read(base_phy + DDR_PHY_ACPHYCTL7);
ACPHYCTL7 = ddr_read(base_phy + DDR_PHY_ACPHYCTL7);
for (i = 0; i < ddrtr_data->byte_num << 3; i++) {
DDR_INFO("Byte[%x] Write[%x][%x] Read[%x][%x]",
i, ddrtr_data->write.ddr_bit_result[i], ddrtr_data->write.ddr_bit_best[i],
ddrtr_data->read.ddr_bit_result[i], ddrtr_data->read.ddr_bit_best[i]);
}
for (i = 0; i < DDR_PHY_CA_MAX; i++) {
if (ddrtr_data->ca_addr[i] != 0)
DDR_INFO("CA[%x] Range[%x]", i, ddrtr_data->ca_addr[i]);
}
/* WDQS */
for (i = 0; i < byte_num; i++) {
DDR_INFO("[%x = %x] WDQS Byte(%x) ",
base_phy + DDR_PHY_DXWDQSDLY(rank, i),
ddr_read(base_phy + DDR_PHY_DXWDQSDLY(rank, i)), i);
}
/* WDQ Phase */
for (i = 0; i < byte_num; i++) {
DDR_INFO("[%x = %x] WDQ Phase Byte(%x)",
base_phy + DDR_PHY_DXNWDQDLY(rank, i),
ddr_read(base_phy + DDR_PHY_DXNWDQDLY(rank, i)), i);
}
/* WDQ BDL */
for (i = 0; i < byte_num; i++) {
/* DQ0-DQ3 */
DDR_INFO("[%x = %x] WDQ BDL DQ(%x-%x)",
base_phy + DDR_PHY_DXNWDQNBDL0(rank, i),
ddr_read(base_phy + DDR_PHY_DXNWDQNBDL0(rank, i)),
(i << 3), ((i << 3) + 3));
/* DQ4-DQ7 */
DDR_INFO("[%x = %x] WDQ BDL DQ(%x-%x)",
base_phy + DDR_PHY_DXNWDQNBDL1(rank, i),
ddr_read(base_phy + DDR_PHY_DXNWDQNBDL1(rank, i)),
((i << 3) + 4), ((i << 3) + 7));
}
/* WDM */
for (i = 0; i < byte_num; i++) {
DDR_INFO("[%x = %x] WDM Byte(%x)",
base_phy + DDR_PHY_DXNWDQNBDL2(rank, i),
ddr_read(base_phy + DDR_PHY_DXNWDQNBDL2(rank, i)), i);
}
/* Write DO/DOS OE */
for (i = 0; i < byte_num; i++) {
DDR_INFO("[%x = %x] Write DQ/DQS OE Byte(%x)",
base_phy + DDR_PHY_DXNOEBDL(rank, i),
ddr_read(base_phy + DDR_PHY_DXNOEBDL(rank, i)), i);
}
/* RDQS */
for (i = 0; i < byte_num; i++) {
DDR_INFO("[%x = %x] RDQS Byte(%x)",
base_phy + DDR_PHY_DXNRDQSDLY(i),
ddr_read(base_phy + DDR_PHY_DXNRDQSDLY(i)), i);
}
/* RDQ BDL */
for (i = 0; i < byte_num; i++) {
/* DQ0-DQ3 */
DDR_INFO("[%x = %x] RDQ BDL DQ(%x-%x)",
base_phy + DDR_PHY_DXNRDQNBDL0(rank, i),
ddr_read(base_phy + DDR_PHY_DXNRDQNBDL0(rank, i)),
(i << 3), ((i << 3) + 3));
/* DQ4-DQ7 */
DDR_INFO("[%x = %x] RDQ BDL DQ(%x-%x)",
base_phy + DDR_PHY_DXNRDQNBDL1(rank, i),
ddr_read(base_phy + DDR_PHY_DXNRDQNBDL1(rank, i)),
((i << 3) + 4), ((i << 3) + 7));
}
/* Gate */
for (i = 0; i < byte_num; i++) {
DDR_INFO("[%x = %x] Gate Byte(%x)",
base_phy + DDR_PHY_DXNRDQSGDLY(rank, i),
ddr_read(base_phy + DDR_PHY_DXNRDQSGDLY(rank, i)), i);
}
DDR_INFO("[%x = %x] CS",
base_phy + DDR_PHY_ACCMDBDL2,
ddr_read(base_phy + DDR_PHY_ACCMDBDL2));
DDR_INFO("[%x = %x] CLK",
base_phy + DDR_PHY_ACPHYCTL7, ACPHYCTL7);
DDR_PHY_SWITCH_RANK(base_phy, rank);
/* HOST Vref */
DDR_PHY_VREF_HOST_DISPLAY_CMD(base_phy, rank, byte_num);
/* DRAM Vref */
DDR_PHY_VREF_DRAM_DISPLAY_CMD(base_phy, byte_num);
/* Addr Phase */
DDR_PHY_ADDRPH_DISPLAY_CMD(base_phy);
/* Addr BDL */
DDR_PHY_ADDRBDL_DISPLAY_CMD(base_phy);
/* DCC */
DDR_PHY_DCC_DISPLAY_CMD(base_phy);
}
static void dump_result_by_rank(struct ddr_training_result_st *ddrtr_result,
unsigned int phy_index, unsigned int rank_index)
{
unsigned int mask = 1 << phy_index; /* DDR_BYPASS_PHY0_MASK DDR_BYPASS_PHY1_MASK */
struct rank_data_st *rank_st = &ddrtr_result->phy_st[phy_index].rank_st[rank_index];
if (rank_st->item & mask)
return;
DDR_INFO("PHY[%x] RANK[%x]:", phy_index, rank_index);
dump_result(&rank_st->ddrtr_data);
}
static void dump_result_by_phy(struct ddr_training_result_st *ddrtr_result,
unsigned int phy_index)
{
int i;
struct phy_data_st *phy_st = &ddrtr_result->phy_st[phy_index];
for (i = 0; i < phy_st->rank_num; i++) {
dump_result_by_rank(ddrtr_result, phy_index, i);
}
}
/* Display ddr training result before return to DDR */
static void dump_result_all(struct ddr_training_result_st *ddrtr_result)
{
int i;
for (i = 0; i < ddrtr_result->phy_num; i++) {
dump_result_by_phy(ddrtr_result, i);
}
}
int ddr_training_cmd_func(struct ddr_cfg_st *cfg)
{
int result = 0;
struct ddr_cmd_st *cmd_st = (struct ddr_cmd_st *)cfg->cmd_st;
unsigned int item = cfg->cur_item;
DDR_DEBUG("DDR training cmd[%x]", cmd_st->cmd);
switch (cmd_st->cmd) {
case DDR_TRAINING_CMD_SW:
/* todo */
result = ddr_dataeye_training_func(cfg);
result += ddr_vref_training_func(cfg);
break;
case DDR_TRAINING_CMD_DATAEYE:
result = ddr_dataeye_training_func(cfg);
break;
case DDR_TRAINING_CMD_HW:
result = ddr_hw_training_if();
break;
case DDR_TRAINING_CMD_MPR:
result = ddr_mpr_training_func(cfg);
break;
case DDR_TRAINING_CMD_WL:
result = ddr_wl_func(cfg);
break;
case DDR_TRAINING_CMD_GATE:
result = ddr_gating_func(cfg);
break;
case DDR_TRAINING_CMD_VREF:
result = ddr_vref_training_func(cfg);
break;
case DDR_TRAINING_CMD_AC:
result = ddr_ac_training_func(cfg);
break;
case DDR_TRAINING_CMD_LPCA:
result = ddr_lpca_training_func(cfg);
break;
case DDR_TRAINING_CMD_SW_NO_WL:
/* wl bypass */
ddr_write(item | DDR_BYPASS_WL_MASK,
DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG);
result = ddr_dataeye_training_func(cfg);
result += ddr_vref_training_func(cfg);
/* restore cfg */
ddr_write(item,
DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG);
break;
case DDR_TRAINING_CMD_CONSOLE:
result = ddr_training_console_if((void *)&ddrt_result_sram);
break;
default:
result = -1;
break;
}
return result;
}
/* DDR training command entry. Call by cmd_ddr_handle(). */
struct ddr_training_result_st *ddr_training_cmd_entry(
struct ddr_cmd_st *cmd_st)
{
int result = 0;
struct ddr_cfg_st ddr_cfg;
struct ddr_cfg_st *cfg = &ddr_cfg;
struct ddr_cmd_st cmd_in_sram;
ddr_training_addr_start = cmd_st->start;
ddr_training_addr_end = cmd_st->start + cmd_st->length;
ddr_print_level = cmd_st->level;
DDR_INFO("DDR Training Version: "DDR_TRAINING_VER);
DDR_DEBUG("DDR training command entry. Sysctl[%x = %x]",
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG),
ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG));
#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC
DDR_DEBUG("Rank1 Sysctl[%x = %x]",
(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC),
ddr_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC));
#endif
ddr_training_cfg_init(cfg);
ddrtr_memcpy(&cmd_in_sram, cmd_st, sizeof(struct ddr_cmd_st));
cfg->cmd_st = (void *)&cmd_in_sram;
cfg->res_st = (void *)&ddrt_result_sram;
ddr_training_result_init(cfg, &ddrt_result_sram);
if (cmd_st->cmd == DDR_TRAINING_CMD_HW)
result = ddr_hw_training(cfg);
else if (cmd_st->cmd == DDR_TRAINING_CMD_PCODE)
result = ddr_pcode_training(cfg);
else if (cmd_st->cmd == DDR_TRAINING_CMD_DCC)
result = ddr_dcc_training_func(cfg);
else if (cmd_st->cmd == DDR_TRAINING_CMD_CONSOLE)
result = ddr_training_console_if((void *)&ddrt_result_sram);
else
result = ddr_training_all(cfg);
dump_result_all(&ddrt_result_sram);
if (!result) {
return &ddrt_result_sram;
} else {
DDR_DEBUG("DDR training result[%x]", result);
return 0;
}
}