blob: e77ec7cb1f5238f70a82e3bd59967ba6d8ba0cfa [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright 2015 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <arch/io.h>
#include <assert.h>
#include <delay.h>
#include <stdlib.h>
#include <soc/addressmap.h>
#include <soc/dramc_common.h>
#include <soc/dramc_register.h>
#include <soc/dramc_pi_api.h>
#include <soc/emi.h>
static u8 opt_gw_coarse_value[CHANNEL_NUM][DUAL_RANKS];
static u8 opt_gw_fine_value[CHANNEL_NUM][DUAL_RANKS];
static s8 wrlevel_dqs_dly[CHANNEL_NUM][DQS_NUMBER];
void sw_impedance_cal(u32 channel,
const struct mt8173_sdram_params *sdram_params)
{
u32 mask, value;
const struct mt8173_calib_params *params = &sdram_params->calib_params;
dramc_dbg_msg("[Imp Calibration] DRVP:%d\n", params->impedance_drvp);
dramc_dbg_msg("[Imp Calibration] DRVN:%d\n", params->impedance_drvn);
mask = 0xf << 28 | 0xf << 24 | 0xf << 12 | 0xf << 8; /* driving */
value = params->impedance_drvp << 28 | params->impedance_drvn << 24 |
params->impedance_drvp << 12 | params->impedance_drvn << 8;
/* DQS and DQ */
clrsetbits_le32(&ch[channel].ao_regs->iodrv6, mask, value);
/* CLK and CMD */
clrsetbits_le32(&ch[channel].ao_regs->drvctl1, mask, value);
clrsetbits_le32(&ch[channel].ddrphy_regs->drvctl1, mask, value);
/* DQ_2 and CMD_2 */
clrsetbits_le32(&ch[channel].ao_regs->iodrv4, mask, value);
/* disable impcal calibration */
clrbits_le32(&ch[channel].ao_regs->impcal, 1 << IMP_CALI_ENP_SHIFT |
1 << IMP_CALI_ENN_SHIFT |
1 << IMP_CALI_EN_SHIFT |
0xf << IMP_CALI_DRVP_SHIFT |
0xf << IMP_CALI_DRVN_SHIFT);
}
void ca_training(u32 channel, const struct mt8173_sdram_params *sdram_params)
{
const struct mt8173_calib_params *params = &sdram_params->calib_params;
u32 i, ca_shift_avg32 = 0;
s8 ca_max_center, ca_shift_avg8 = 0, order, ca_shift[CATRAINING_NUM];
s8 shift[CATRAINING_NUM] = {
CMDDLY0_RA2_SHIFT, CMDDLY1_RA7_SHIFT, CMDDLY3_BA0_SHIFT,
CMDDLY3_BA1_SHIFT, CMDDLY3_BA2_SHIFT, CMDDLY4_RAS_SHIFT,
CMDDLY4_CAS_SHIFT, CMDDLY5_RA13_SHIFT, CMDDLY5_WE_SHIFT
};
s8 ca_order[CHANNEL_NUM][CATRAINING_NUM] = {
{ 7, 5, 6, 1, 3, 0, 9, 8, 2, 4},
{ 2, 0, 3, 7, 5, 9, 4, 1, 6, 8}
};
s8 cmd_order[CATRAINING_NUM] = {
0, 1, 3, 3, 3, 4, 4, 5, 5
};
for (i = 0; i < CATRAINING_NUM; i++) {
ca_shift[i] = params->ca_train[channel][i];
ca_shift_avg8 += ca_shift[i];
}
/* CA pins align the center */
ca_max_center = params->ca_train_center[channel];
/* set CA pins output delay */
for (i = 0; i < (CATRAINING_NUM - 1); i++) {
order = ca_order[channel][i];
clrsetbits_le32(&ch[channel].ddrphy_regs->cmddly[cmd_order[i]],
0xf << shift[i], ca_shift[order] << shift[i]);
}
order = ca_order[channel][9];
clrsetbits_le32(&ch[channel].ddrphy_regs->dqscal0,
0xf << DQSCAL0_RA14_SHIFT,
ca_shift[order] << DQSCAL0_RA14_SHIFT);
/* CKE and CS delay */
ca_shift_avg32 = (u32)(ca_shift_avg8 + (CATRAINING_NUM >> 1));
ca_shift_avg32 /= (u32) CATRAINING_NUM;
/* CKEDLY */
clrsetbits_le32(&ch[channel].ddrphy_regs->cmddly[4],
0x1f << CMDDLY4_CS_SHIFT |
0x1f << CMDDLY4_CKE_SHIFT,
ca_shift_avg32 << CMDDLY4_CS_SHIFT |
ca_shift_avg32 << CMDDLY4_CKE_SHIFT);
/* CKE1DLY */
clrsetbits_le32(&ch[channel].ao_regs->dqscal1,
0x1f << DQSCAL1_CKE1_SHIFT,
ca_shift_avg32 << DQSCAL1_CKE1_SHIFT);
/* CS1DLY */
clrsetbits_le32(&ch[channel].ddrphy_regs->padctl1,
0xf << PADCTL1_CS1_SHIFT,
ca_shift_avg32 << PADCTL1_CS1_SHIFT);
/* set max center into clk output delay */
clrsetbits_le32(&ch[channel].ddrphy_regs->padctl1,
0xf << PADCTL1_CLK_SHIFT,
ca_max_center << PADCTL1_CLK_SHIFT);
dramc_dbg_msg("=========================================\n");
dramc_dbg_msg(" [Channel %d] CA training\n", channel);
dramc_dbg_msg("=========================================\n");
for (i = 0; i < CATRAINING_NUM; i++)
dramc_dbg_msg("[CA] CA %d\tShift %d\n", i, ca_shift[i]);
dramc_dbg_msg("[CA] Reg CMDDLY4 = %xh\n",
read32(&ch[channel].ddrphy_regs->cmddly[4]));
dramc_dbg_msg("[CA] Reg DQSCAL1 = %xh\n",
read32(&ch[channel].ao_regs->dqscal1));
dramc_dbg_msg("[CA] Reg PADCTL1 = %xh\n",
read32(&ch[channel].ddrphy_regs->padctl1));
}
void write_leveling(u32 channel, const struct mt8173_sdram_params *sdram_params)
{
u8 i, byte_i;
u32 value;
for (i = 0; i < DQS_NUMBER; i++)
wrlevel_dqs_dly[channel][i] =
sdram_params->calib_params.wr_level[channel][i];
/* DQS */
value = 0;
for (i = 0; i < DQS_NUMBER; i++) {
value += ((u32)wrlevel_dqs_dly[channel][i]) << (4 * i);
}
write32(&ch[channel].ddrphy_regs->padctl3, value);
/* DQM */
clrsetbits_le32(&ch[channel].ddrphy_regs->padctl2, MASK_PADCTL2_32BIT,
(value << PADCTL2_SHIFT) & MASK_PADCTL2_32BIT);
/* DQ */
for (byte_i = 0; byte_i < DQS_NUMBER; byte_i++) {
value = 0;
for (i = 0; i < DQS_BIT_NUMBER; i++) {
s8 val = wrlevel_dqs_dly[channel][byte_i];
value += (((u32)val) << (4 * i));
}
write32(&ch[channel].ddrphy_regs->dqodly[byte_i], value);
}
dramc_dbg_msg("========================================\n");
dramc_dbg_msg("[Channel %d] dramc_write_leveling_swcal\n", channel);
dramc_dbg_msg("========================================\n");
dramc_dbg_msg("[WL] DQS: %#x",
read32(&ch[channel].ddrphy_regs->padctl3));
dramc_dbg_msg("[WL] DQM: %#x\n",
read32(&ch[channel].ddrphy_regs->padctl2));
for (byte_i = 0; byte_i < DQS_NUMBER; byte_i++)
dramc_dbg_msg("[WL] DQ byte%d: %#x\n", byte_i,
read32(&ch[channel].ddrphy_regs->dqodly[byte_i]));
}
static void set_gw_coarse_factor(u32 channel, u8 curr_val)
{
u8 curr_val_p1, selph2_dqsgate, selph2_dqsgate_p1;
u32 coarse_tune_start = curr_val >> 2;
if (coarse_tune_start > 3) {
coarse_tune_start -= 3;
} else {
if (coarse_tune_start) {
coarse_tune_start = 1;
}
}
if (coarse_tune_start > 15) {
coarse_tune_start = 15;
}
curr_val_p1 = curr_val + 2; /* diff is 0.5T */
/* Rank 0 P0/P1 coarse tune settings */
clrsetbits_le32(&ch[channel].ao_regs->dqsctl1,
0xf << DQSCTL1_DQSINCTL_SHIFT,
coarse_tune_start << DQSCTL1_DQSINCTL_SHIFT &
0xf << DQSCTL1_DQSINCTL_SHIFT);
/* DQSINCTL does not have P1. */
/* Need to use TXDLY_DQSGATE/TXDLY_DQSGATE_P1 to set */
/* different 1 M_CK coarse tune values for P0 & P1. */
selph2_dqsgate = (curr_val >> 2) - coarse_tune_start;
selph2_dqsgate_p1 = (curr_val_p1 >> 2) - coarse_tune_start;
clrsetbits_le32(&ch[channel].ao_regs->selph2,
0x7 << SELPH2_TXDLY_DQSGATE_SHIFT |
0x7 << SELPH2_TXDLY_DQSGATE_P1_SHIFT,
selph2_dqsgate << SELPH2_TXDLY_DQSGATE_SHIFT |
selph2_dqsgate_p1 << SELPH2_TXDLY_DQSGATE_P1_SHIFT);
/* dly_DQSGATE and dly_DQSGATE_P1 */
clrsetbits_le32(&ch[channel].ao_regs->selph5,
0x3 << SELPH5_DLY_DQSGATE_SHIFT |
0x3 << SELPH5_DLY_DQSGATE_P1_SHIFT,
(curr_val & 0x3) << SELPH5_DLY_DQSGATE_SHIFT |
(curr_val_p1 & 0x3) << SELPH5_DLY_DQSGATE_P1_SHIFT);
}
static void set_gw_fine_factor(u32 channel, u8 curr_val, u8 rank)
{
u32 set = curr_val & (0x7f << DQSIEN_DQS0IEN_SHIFT);
clrsetbits_le32(&ch[channel].ao_regs->dqsien[rank],
0x7f << DQSIEN_DQS0IEN_SHIFT |
0x7f << DQSIEN_DQS1IEN_SHIFT |
0x7f << DQSIEN_DQS2IEN_SHIFT |
0x7f << DQSIEN_DQS3IEN_SHIFT,
set << DQSIEN_DQS0IEN_SHIFT |
set << DQSIEN_DQS1IEN_SHIFT |
set << DQSIEN_DQS2IEN_SHIFT |
set << DQSIEN_DQS3IEN_SHIFT);
}
static void set_gw_coarse_factor_rank1(u32 channel, u8 curr_val, u8 dqsinctl)
{
u8 curr_val_p1, r1dqsgate, r1dqsgate_p1;
curr_val_p1 = curr_val + 2; /* diff is 0.5T */
clrsetbits_le32(&ch[channel].ao_regs->dqsctl2,
0xf << DQSCTL2_DQSINCTL_SHIFT,
dqsinctl << DQSCTL2_DQSINCTL_SHIFT);
/* TXDLY_R1DQSGATE and TXDLY_R1DQSGATE_P1 */
r1dqsgate = (curr_val >> 2) - dqsinctl;
r1dqsgate_p1 = (curr_val_p1 >> 2) - dqsinctl;
clrsetbits_le32(&ch[channel].ao_regs->selph6_1,
0x7 << SELPH6_1_TXDLY_R1DQSGATE_SHIFT |
0x7 << SELPH6_1_TXDLY_R1DQSGATE_P1_SHIFT,
r1dqsgate << SELPH6_1_TXDLY_R1DQSGATE_SHIFT |
r1dqsgate_p1 << SELPH6_1_TXDLY_R1DQSGATE_P1_SHIFT);
/* dly_R1DQSGATE and dly_R1DQSGATE_P1 */
clrsetbits_le32(&ch[channel].ao_regs->selph6_1,
0x3 << SELPH6_1_DLY_R1DQSGATE_SHIFT |
0x3 << SELPH6_1_DLY_R1DQSGATE_P1_SHIFT,
(curr_val & 0x3) << SELPH6_1_DLY_R1DQSGATE_SHIFT |
(curr_val_p1 & 0x3) << SELPH6_1_DLY_R1DQSGATE_P1_SHIFT);
}
static void dqs_gw_counter_reset(u32 channel)
{
/* reset dqs counter (1 to 0) */
setbits_le32(&ch[channel].ao_regs->spcmd, 1 << SPCMD_DQSGCNTRST_SHIFT);
clrbits_le32(&ch[channel].ao_regs->spcmd, 1 << SPCMD_DQSGCNTRST_SHIFT);
dramc_phy_reset(channel);
}
static int dqs_gw_test(u32 channel)
{
u32 coarse_result01, coarse_result23;
/* read data counter reset in PHY layer */
dqs_gw_counter_reset(channel);
/* use audio pattern to run the test */
dramc_engine2(channel, TE_OP_READ_CHECK, DQS_GW_PATTERN2,
DQS_GW_PATTERN1 | DQS_GW_TE_OFFSET, 1, 0);
/* get coarse result of DQS0, 1, 2, 3 */
coarse_result01 = read32(&ch[channel].nao_regs->dqsgnwcnt[0]);
coarse_result23 = read32(&ch[channel].nao_regs->dqsgnwcnt[1]);
if (coarse_result01 == DQS_GW_GOLD_COUNTER_32BIT &&
coarse_result23 == DQS_GW_GOLD_COUNTER_32BIT)
return 1;
return 0;
}
static u8 dqs_gw_fine_tune_calib(u32 channel, u8 fine_val)
{
u8 i, opt_fine_val;
s8 delta[7] = {-48, -32, -16, 0, 16, 32, 48};
int matches = 0, sum = 0;
/* fine tune range from 0 to 127 */
fine_val = min(max(fine_val, 0 - delta[0]), 127 - delta[6]);
/* test gw fine tune */
for (i = 0; i < ARRAY_SIZE(delta); i++) {
opt_fine_val = fine_val + delta[i];
set_gw_fine_factor(channel, opt_fine_val, 0);
if (dqs_gw_test(channel)) {
matches++;
sum += delta[i];
}
}
if (matches == 0) {
die("[GW] ERROR, Fine-Tuning failed.\n");
}
opt_fine_val = fine_val + (sum / matches);
return opt_fine_val;
}
static u8 dqs_gw_coarse_tune_calib(u32 channel, u8 coarse_val)
{
u8 i, opt_coarse_val[3];
s8 gw_ret[3], delta[3] = {0, 1, -1};
for (i = 0; i < 3; i++) {
/* adjust gw coarse tune value */
opt_coarse_val[i] = coarse_val + delta[i];
set_gw_coarse_factor(channel, opt_coarse_val[i]);
/* get gw test result */
gw_ret[i] = dqs_gw_test(channel);
/* judge test result */
if (gw_ret[i] != 0)
return opt_coarse_val[i];
}
/* abnormal test result, set to default coarse tune value */
printk(BIOS_ERR, "[GW] ERROR, No found coarse tune!!!\n");
return coarse_val;
}
void rx_dqs_gating_cal(u32 channel, u8 rank,
const struct mt8173_sdram_params *sdram_params)
{
u8 gw_coarse_val, gw_fine_val;
/* disable HW gating */
clrbits_le32(&ch[channel].ao_regs->dqscal0,
1 << DQSCAL0_STBCALEN_SHIFT);
/* enable DQS gating window counter */
setbits_le32(&ch[channel].ao_regs->dqsctl1,
1 << DQSCTL1_DQSIENMODE_SHIFT);
setbits_le32(&ch[channel].ao_regs->spcmd,
1 << SPCMD_DQSGCNTEN_SHIFT);
/* dual-phase DQS clock gating control enabling */
setbits_le32(&ch[channel].ddrphy_regs->dqsgctl,
1 << DQSGCTL_DQSGDUALP_SHIFT);
/* gating calibration value */
gw_coarse_val = sdram_params->calib_params.gating_win[channel][rank][0];
gw_fine_val = sdram_params->calib_params.gating_win[channel][rank][1];
dramc_dbg_msg("****************************************************\n");
dramc_dbg_msg("Channel %d Rank %d DQS GW Calibration\n", channel, rank);
dramc_dbg_msg("Default (coarse, fine) tune value %d, %d.\n",
gw_coarse_val, gw_fine_val);
dramc_dbg_msg("****************************************************\n");
/* set default coarse and fine value */
set_gw_coarse_factor(channel, gw_coarse_val);
set_gw_fine_factor(channel, gw_fine_val, 0);
/* adjust gw coarse tune */
opt_gw_coarse_value[channel][rank] =
dqs_gw_coarse_tune_calib(channel, gw_coarse_val);
/* set adjusted gw coarse tune */
set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][rank]);
/* adjust gw fine tune */
opt_gw_fine_value[channel][rank] =
dqs_gw_fine_tune_calib(channel, gw_fine_val);
/* set adjusted gw fine tune */
set_gw_fine_factor(channel, opt_gw_fine_value[channel][rank], 0);
/* read data counter reset in PHY layer */
dqs_gw_counter_reset(channel);
/* gating window training result */
printk(BIOS_INFO, "[GW] [Channel %d] [Rank %d] adjusted (coarse, fine) tune value: %d, %d.\n",
channel, rank, opt_gw_coarse_value[channel][rank],
opt_gw_fine_value[channel][rank]);
}
void dual_rank_rx_dqs_gating_cal(u32 channel,
const struct mt8173_sdram_params *sdram_params)
{
u32 dqsinctl;
/* rank 0 gw calibration */
rx_dqs_gating_cal(channel, 0, sdram_params);
/* get dqsinctl after rank 0 calibration */
dqsinctl = read32(&ch[channel].ao_regs->dqsctl1);
dqsinctl = (dqsinctl >> DQSCTL1_DQSINCTL_SHIFT) & (0xf << 0);
/* swap cs0 and cs1 */
setbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN);
/* rank 1 gw calibration */
rx_dqs_gating_cal(channel, 1, sdram_params);
/* set rank 1 coarse tune and fine tune */
set_gw_coarse_factor_rank1(channel, opt_gw_coarse_value[channel][1],
dqsinctl);
set_gw_fine_factor(channel, opt_gw_fine_value[channel][1], 1);
/* swap cs back */
clrbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN);
/* set rank 0 coarse tune and fine tune back */
set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][0]);
set_gw_fine_factor(channel, opt_gw_fine_value[channel][0], 0);
}
void dramc_rankinctl_config(u32 channel,
const struct mt8173_sdram_params *sdram_params)
{
u32 value;
if (is_dual_rank(channel, sdram_params)) {
/* RANKINCTL_ROOT1 = DQSINCTL + reg_TX_DLY_DQSGATE */
value = min(opt_gw_coarse_value[channel][0],
opt_gw_coarse_value[channel][1]) >> 2;
clrsetbits_le32(&ch[channel].ao_regs->dummy, 0xf, value);
/* RANKINCTL = RANKINCTL_ROOT1 */
clrsetbits_le32(&ch[channel].ao_regs->dqscal1,
0xf << 16, value << 16);
}
/* disable per-bank refresh when refresh rate >= 5 */
setbits_le32(&ch[channel].ao_regs->rkcfg,
1 << RKCFG_PBREF_DISBYRATE_SHIFT);
}
u32 dram_k_perbit(u32 channel)
{
u32 err_value = 0x0;
/* use XTALK pattern to run the test */
err_value = dramc_engine2(channel, TE_OP_WRITE_READ_CHECK,
DEFAULT_TEST2_1_CAL, DEFAULT_TEST2_2_CAL,
2, 0);
return err_value;
}
void dramk_check_dqs_win(struct dqs_perbit_dly *p, u8 dly_step, u8 last_step,
u32 fail_bit)
{
s8 dqsdly_pass_win, best_pass_win;
if (fail_bit == 0) {
if (p->first_dqsdly_pass == -1) {
/* first DQS pass delay tap */
p->first_dqsdly_pass = dly_step;
}
if ((p->last_dqsdly_pass == -2) && (dly_step == last_step)) {
/* pass to the last tap */
p->last_dqsdly_pass = dly_step;
dqsdly_pass_win = p->last_dqsdly_pass -
p->first_dqsdly_pass;
best_pass_win = p->best_last_dqsdly_pass -
p->best_first_dqsdly_pass;
if (dqsdly_pass_win > best_pass_win) {
p->best_last_dqsdly_pass = p->last_dqsdly_pass;
p->best_first_dqsdly_pass = p->first_dqsdly_pass;
}
/* clear to find the next pass range if it has */
p->first_dqsdly_pass = -1;
p->last_dqsdly_pass = -2;
}
} else {
if ((p->first_dqsdly_pass != -1) && (p->last_dqsdly_pass == -2)) {
p->last_dqsdly_pass = dly_step - 1;
dqsdly_pass_win = p->last_dqsdly_pass -
p->first_dqsdly_pass;
best_pass_win = p->best_last_dqsdly_pass -
p->best_first_dqsdly_pass;
if (dqsdly_pass_win > best_pass_win) {
p->best_last_dqsdly_pass = p->last_dqsdly_pass;
p->best_first_dqsdly_pass = p->first_dqsdly_pass;
}
/* clear to find the next pass range if it has */
p->first_dqsdly_pass = -1;
p->last_dqsdly_pass = -2;
}
}
}
void dramk_check_dq_win(struct dqs_perbit_dly *p, u8 dly_step, u8 last_step,
u32 fail_bit)
{
s8 dqdly_pass_win, best_pass_win;
if (fail_bit == 0) {
if (p->first_dqdly_pass == -1) {
/* first DQ pass delay tap */
p->first_dqdly_pass = dly_step;
}
if ((p->last_dqdly_pass == -2) && (dly_step == last_step)) {
/* pass to the last tap */
p->last_dqdly_pass = dly_step;
dqdly_pass_win = p->last_dqdly_pass -
p->first_dqdly_pass;
best_pass_win = p->best_last_dqdly_pass -
p->best_first_dqdly_pass;
if (dqdly_pass_win > best_pass_win) {
p->best_last_dqdly_pass = p->last_dqdly_pass;
p->best_first_dqdly_pass = p->first_dqdly_pass;
}
/* clear to find the next pass range if it has */
p->first_dqdly_pass = -1;
p->last_dqdly_pass = -2;
}
} else {
if ((p->first_dqdly_pass != -1) && (p->last_dqdly_pass == -2)) {
p->last_dqdly_pass = dly_step - 1;
dqdly_pass_win = p->last_dqdly_pass -
p->first_dqdly_pass;
best_pass_win = p->best_last_dqdly_pass -
p->best_first_dqdly_pass;
if (dqdly_pass_win > best_pass_win) {
p->best_last_dqdly_pass = p->last_dqdly_pass;
p->best_first_dqdly_pass = p->first_dqdly_pass;
}
/* clear to find the next pass range if it has */
p->first_dqdly_pass = -1;
p->last_dqdly_pass = -2;
}
}
}
u8 dramk_calcu_best_dly(u8 bit, struct dqs_perbit_dly *p, u8 *p_max_byte)
{
u8 fail = 0;
u8 hold, setup;
/* hold time = DQS pass taps */
hold = p->best_last_dqsdly_pass - p->best_first_dqsdly_pass + 1;
/* setup time = DQ pass taps */
setup = p->best_last_dqdly_pass - p->best_first_dqdly_pass + 1;
/* The relationship of setup and hold time of dqs and dq signals
* is represented with delay tap in the following format:
*
* setup time(dq delay) hold time(dqs delay)
* xxxxxxxxxxxxxoooooooo|ooooooooooooooooooooxxxxx
* 15 0 1 15 tap
*/
if (hold > setup) {
/* like this: (setup time != 0) */
/* xxxxxxxxxxxxxoooooooo|ooooooooooooooooooooxxxxx */
/* like this: (setup time == 0) */
/* xxxxxxxxxxxxxxxxxxxxx|xxxooooooooooxxxxxxxxxxxx */
p->best_dqdly = 0;
p->best_dqsdly = (setup != 0)? (hold - setup) / 2:
(hold - setup) / 2 + p->best_first_dqsdly_pass;
if (p->best_dqsdly > *p_max_byte) {
*p_max_byte = p->best_dqsdly;
}
} else if (hold < setup) {
/* like this: (hold time != 0 )*/
/* xxxoooooooooooooooooo|ooooooooxxxxxxxxxxxxxxxxx */
/* like this: (hold time == 0 ) */
/* xxxoooooooooooooooxxx|xxxxxxxxxxxxxxxxxxxxxxxxx */
p->best_dqsdly = 0;
p->best_dqdly = (hold != 0)? (setup - hold) / 2:
(setup - hold) / 2 + p->best_first_dqdly_pass;
} else { /* hold time == setup time */
p->best_dqsdly = 0;
p->best_dqdly = 0;
if (hold == 0) {
/* like this: (mean this bit is error) */
/* xxxxxxxxxxxxxxxxxxxxx|xxxxxxxxxxxxxxxxxxxxxxxxx */
printk(BIOS_ERR, "ERROR, error bit %d, "
"setup_time = hold_time = 0!!\n", bit);
fail = 1;
}
}
dramc_dbg_msg("bit#%d : dq =%d dqs=%d win=%d (%d, %d)\n",
bit, setup, hold, setup + hold,
p->best_dqdly, p->best_dqsdly);
return fail;
}
void clk_duty_cal(u32 channel)
{
u8 max_duty_sel, max_duty;
u32 max_win_size = 0;
max_duty_sel = max_duty = 1;
clrsetbits_le32(&ch[channel].ddrphy_regs->phyclkduty,
0x3 << PHYCLKDUTY_CMDCLKP0DUTYN_SHIFT |
1 << PHYCLKDUTY_CMDCLKP0DUTYP_SHIFT,
1 << PHYCLKDUTY_CMDCLKP0DUTYSEL_SHIFT |
max_duty << PHYCLKDUTY_CMDCLKP0DUTYN_SHIFT);
max_win_size = read32(&ch[channel].ddrphy_regs->phyclkduty);
dramc_dbg_msg("[Channel %d CLK DUTY CALIB] ", channel);
dramc_dbg_msg("Final DUTY_SEL=%d, DUTY=%d, rx window size=%d\n",
max_duty_sel, max_duty, max_win_size);
}
static void set_dle_factor(u32 channel, u8 curr_val)
{
clrsetbits_le32(&ch[channel].ao_regs->ddr2ctl,
0x7 << DDR2CTL_DATLAT_SHIFT,
(curr_val & 0x7) << DDR2CTL_DATLAT_SHIFT);
clrsetbits_le32(&ch[channel].ao_regs->padctl4,
0x1 << PADCTL4_DATLAT3_SHIFT,
((curr_val >> 3) & 0x1) << PADCTL4_DATLAT3_SHIFT);
clrsetbits_le32(&ch[channel].ao_regs->phyctl1,
0x1 << PHYCTL1_DATLAT4_SHIFT,
((curr_val >> 4) & 0x1) << PHYCTL1_DATLAT4_SHIFT);
clrsetbits_le32(&ch[channel].ao_regs->misc,
0x1f << MISC_DATLAT_DSEL_SHIFT,
(curr_val - 8) << MISC_DATLAT_DSEL_SHIFT);
/* optimize bandwidth for HW run time test engine use */
clrsetbits_le32(&ch[channel].ao_regs->misc,
0x1f << MISC_LATNORMP_SHIFT,
(curr_val - 3) << MISC_LATNORMP_SHIFT);
}
void dual_rank_rx_datlat_cal(u32 channel,
const struct mt8173_sdram_params *sdram_params)
{
u8 r0_dle_setting, r1_dle_setting;
/* rank 0 dle calibration */
r0_dle_setting = rx_datlat_cal(channel, 0, sdram_params);
/* swap cs0 and cs1 */
setbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN);
/* set rank 1 coarse tune and fine tune back */
set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][1]);
set_gw_fine_factor(channel, opt_gw_fine_value[channel][1], 0);
/* rank 1 dle calibration */
r1_dle_setting = rx_datlat_cal(channel, 1, sdram_params);
/* set rank 0 coarse tune and fine tune back */
set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][0]);
set_gw_fine_factor(channel, opt_gw_fine_value[channel][0], 0);
/* swap cs back */
clrbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN);
/* output dle setting of rank 0 and 1 */
dramc_dbg_msg("[DLE] Rank 0 DLE calibrated setting = %xh.\n"
"[DLE] Rank 1 DLE calibrated setting = %xh.\n",
r0_dle_setting, r1_dle_setting);
if (r1_dle_setting < r0_dle_setting) {
/* compare dle setting of two ranks */
dramc_dbg_msg("[DLE] rank 0 > rank 1. set to rank 0.\n");
/* case 1: set rank 0 dle setting */
set_dle_factor(channel, r0_dle_setting);
} else {
/* compare dle setting of two ranks */
dramc_dbg_msg("[DLE] rank 0 < rank 1. use rank 1.\n");
/* case 2: set rank 1 dle setting */
set_dle_factor(channel, r1_dle_setting);
}
}
u8 rx_datlat_cal(u32 channel, u8 rank,
const struct mt8173_sdram_params *sdram_params)
{
u8 i, best_step;
u32 err[DLE_TEST_NUM];
dramc_dbg_msg("=========================================\n");
dramc_dbg_msg("[Channel %d] [Rank %d] DATLAT calibration\n",
channel, rank);
dramc_dbg_msg("=========================================\n");
clrbits_le32(&ch[channel].ao_regs->mckdly,
0x11 << MCKDLY_DQIENQKEND_SHIFT |
0x1 << MCKDLY_DQIENLAT_SHIFT);
/* set dle calibration initial value */
best_step = sdram_params->calib_params.datlat_ucfirst + 1;
/* do dle calibration test */
for (i = 0; i < DLE_TEST_NUM; i++) {
set_dle_factor(channel, best_step - i);
err[i] = dramc_engine2(channel, TE_OP_WRITE_READ_CHECK,
DEFAULT_TEST2_1_CAL,
DEFAULT_TEST2_2_CAL, 2, 0);
}
if (err[0]) {
/* dle test error */
printk(BIOS_ERR, "[DLE] CH:%d calibration ERROR CMP_ERR =%xh,\n",
channel, err[0]);
} else {
/* judge dle test result */
for (i = 0; i < DLE_TEST_NUM; i++) {
if (!err[i] && (i + 1 == DLE_TEST_NUM || err[i + 1])) {
/* dle test ok */
best_step -= (i - 1);
break;
}
}
}
/* Default dle value is set when test error (error recovery).
* Others, adjusted dle calibration value is set normally.
*/
set_dle_factor(channel, best_step);
dramc_dbg_msg("[DLE] adjusted value = %#x\n", best_step);
return best_step;
}
void tx_delay_for_wrleveling(u32 channel,
struct dqs_perbit_dly *dqdqs_perbit_dly,
u8 *max_dqsdly_byte, u8 *ave_dqdly_byte)
{
s8 i, delta, index, max_taps;
max_taps = MAX_DQDLY_TAPS - 1;
for (i = 0; i < DATA_WIDTH_32BIT; i++) {
index = i / DQS_BIT_NUMBER;
if (i % DQS_BIT_NUMBER == 0)
dramc_dbg_msg("DQS%d: %d\n", index,
wrlevel_dqs_dly[channel][index]);
if (max_dqsdly_byte[index] <= wrlevel_dqs_dly[channel][index]) {
/* set diff value (delta) */
delta = wrlevel_dqs_dly[channel][index] -
max_dqsdly_byte[index];
dqdqs_perbit_dly[i].best_dqdly += delta;
/* max limit to 15 */
if (dqdqs_perbit_dly[i].best_dqdly > max_taps)
dqdqs_perbit_dly[i].best_dqdly = max_taps;
if ((i + 1) % DQS_BIT_NUMBER == 0) {
/* DQS */
max_dqsdly_byte[index] =
wrlevel_dqs_dly[channel][index];
/* DQM */
ave_dqdly_byte[index] += delta;
/* max limit to 15 */
if (ave_dqdly_byte[index] > max_taps)
ave_dqdly_byte[index] = max_taps;
}
} else if (i % DQS_BIT_NUMBER == 0) {
/* max_dqsdly_byte[j] > wrlevel_dqs_dly[channel][j]
* Originally, we should move clk and CA delay.
* Then, do GW calibration again. However, DQ/DQS
* skew should not be large in MT8173, so we sacrifice
* the Clk/DQS margin by keeping the clk out delay.
*/
printk(BIOS_ERR, "[Warning] DQSO %d in TX "
"per-bit = %d > DQSO %d in WL = %d ",
index, max_dqsdly_byte[index], index,
wrlevel_dqs_dly[channel][index]);
}
}
}
static void set_rx_dly_factor(u32 channel, u32 curr_val, u8 type)
{
u32 i, value = 0;
for (i = 0; i < DQS_NUMBER; i++)
value += (curr_val << (8 * i));
switch (type) {
case RX_DQS:
write32(&ch[channel].ao_regs->r0deldly, value);
break;
case RX_DQ:
for (i = 0; i < DATA_WIDTH_32BIT; i += 4)
write32(&ch[channel].ao_regs->dqidly[i/4], value);
break;
}
}
static void set_tx_dly_factor(u32 channel, u32 curr_val, u8 type)
{
u32 i, bit_num, value = 0;
bit_num = (type == TX_DQ)? DQS_BIT_NUMBER: DQS_NUMBER;
for (i = 0; i < bit_num; i++)
value += (curr_val << (4 * i));
switch (type) {
case TX_DQS:
write32(&ch[channel].ddrphy_regs->padctl3, value);
break;
case TX_DQM:
write32(&ch[channel].ddrphy_regs->padctl2, value);
break;
case TX_DQ:
for (i = 0; i < DQS_NUMBER; i++)
write32(&ch[channel].ddrphy_regs->dqodly[i], value);
break;
}
}
static void set_dly_factor(u32 channel, u8 stage, u8 type, u8 dly)
{
switch (stage | type << 1) {
/* set delay for DQ/DQM/DQS by setup/hold stage and window type */
case STAGE_SETUP_TX_WIN:
/* set DQ/DQM delay for tx window */
set_tx_dly_factor(channel, dly, TX_DQ);
set_tx_dly_factor(channel, dly, TX_DQM);
break;
case STAGE_SETUP_RX_WIN:
/* set DQ delay for rx window */
set_rx_dly_factor(channel, dly, RX_DQ);
break;
case STAGE_HOLD_TX_WIN:
/* set DQS delay for tx window */
set_tx_dly_factor(channel, dly, TX_DQS);
break;
case STAGE_HOLD_RX_WIN:
/* set DQS delay for rx window */
set_rx_dly_factor(channel, dly, RX_DQS);
break;
}
}
static void set_rx_best_dly_factor(u32 channel,
struct dqs_perbit_dly *dqdqs_perbit_dly,
u8 *max_dqsdly_byte)
{
u32 i, value = 0;
for (i = 0; i < DQS_NUMBER; i++)
value += (((u32)max_dqsdly_byte[i]) << (8 * i));
write32(&ch[channel].ao_regs->r0deldly, value);
write32(&ch[channel].ao_regs->r1deldly, value);
dramc_dbg_msg("[RX] DQS Reg R0DELDLY=%xh\n",
read32(&ch[channel].ao_regs->r0deldly));
dramc_dbg_msg("[RX] DQS Reg R1DELDLY=%xh\n",
read32(&ch[channel].ao_regs->r1deldly));
for (i = 0; i < DATA_WIDTH_32BIT; i += 4) {
/* every 4bit dq have the same delay register address */
value = ((u32)dqdqs_perbit_dly[i].best_dqdly) +
(((u32)dqdqs_perbit_dly[i + 1].best_dqdly) << 8) +
(((u32)dqdqs_perbit_dly[i + 2].best_dqdly) << 16) +
(((u32)dqdqs_perbit_dly[i + 3].best_dqdly) << 24);
write32(&ch[channel].ao_regs->dqidly[i / 4], value);
dramc_dbg_msg("[RX] DQ DQIDLY%d = %xh\n", (i + 4) / 4, value);
}
}
static void set_tx_best_dly_factor(u32 channel,
struct dqs_perbit_dly *dqdqs_perbit_dly,
u8 *max_dqsdly_byte, u8 *ave_dqdly_byte)
{
u32 bit, value, shift, dqs_index = 0;
value = 0;
for (bit = 0; bit < DQS_NUMBER; bit++) {
value += (((u32)max_dqsdly_byte[bit]) << (4 * bit));
}
write32(&ch[channel].ddrphy_regs->padctl3, value);
dramc_dbg_msg("[TX] DQS PADCTL3 Reg = %#x\n", value);
/* DQ delay */
for (bit = 0; bit < DATA_WIDTH_32BIT; bit++) {
/* every 8 DQ reset */
if (bit % DQS_BIT_NUMBER == 0) {
value = 0;
dqs_index = bit / DQS_BIT_NUMBER;
}
/* 4 bits field for each DQ */
shift = 4 * (bit % DQS_BIT_NUMBER);
value += (((u32)(dqdqs_perbit_dly[bit].best_dqdly)) << shift);
/* each register is with 8 DQ */
if ((bit + 1) % DQS_BIT_NUMBER == 0) {
write32(&ch[channel].ddrphy_regs->dqodly[dqs_index], value);
dramc_dbg_msg("[TX] DQ DQ0DLY%d = %xh\n",
dqs_index + 1, value);
}
}
/* DQM delay */
value = read32(&ch[channel].ddrphy_regs->padctl2);
value &= MASK_PADCTL2;
for (bit = 0; bit < DQS_NUMBER; bit++) {
value += (((u32)ave_dqdly_byte[bit]) << (4 * bit));
}
write32(&ch[channel].ddrphy_regs->padctl2, value);
dramc_dbg_msg("[TX] DQM PADCTL2 Reg = %#x\n", value);
}
void perbit_window_cal(u32 channel, u8 type)
{
u8 i, dly, bit, max_dqs_taps, fail = 0;
u8 max_dqsdly_byte[DQS_NUMBER], ave_dqdly_byte[DQS_NUMBER];
u32 err_value, fail_bit, max_limit, index;
struct dqs_perbit_dly dqdqs_perbit_dly[DQ_DATA_WIDTH];
dramc_dbg_msg("\n[Channel %d] %s DQ/DQS per bit :\n",
channel, (type == TX_WIN)? "TX": "RX");
if (type == TX_WIN)
dramc_phy_reset(channel);
for (i = 0; i < DATA_WIDTH_32BIT; i++) {
dqdqs_perbit_dly[i].first_dqdly_pass = -1;
dqdqs_perbit_dly[i].last_dqdly_pass = -2;
dqdqs_perbit_dly[i].first_dqsdly_pass = -1;
dqdqs_perbit_dly[i].last_dqsdly_pass = -2;
dqdqs_perbit_dly[i].best_first_dqdly_pass = -1;
dqdqs_perbit_dly[i].best_last_dqdly_pass = -2;
dqdqs_perbit_dly[i].best_first_dqsdly_pass = -1;
dqdqs_perbit_dly[i].best_last_dqsdly_pass = -2;
}
/* 1. delay DQ ,find the pass widnow (left boundary)
* 2. delay DQS find the pass window (right boundary)
* 3. find the best DQ / DQS to satify the middle value
* of the overall pass window per bit
* 4. set DQS delay to the max per byte, delay DQ to de-skew
*/
/* 1. set DQS delay to 0 first */
set_dly_factor(channel, STAGE_HOLD, type, FIRST_DQS_DELAY);
dramc_dbg_msg("----------------------------------"
"--------------------\n");
dramc_dbg_msg("Start DQ delay to find pass range,"
"DQS delay fixed to %#x...\n", FIRST_DQS_DELAY);
dramc_dbg_msg("----------------------------------"
"-------------------\n");
dramc_dbg_msg("x-axis is bit #; y-axis is DQ delay (%d~%d)\n",
FIRST_DQ_DELAY, MAX_DQDLY_TAPS - 1);
/* delay DQ from 0 to 15 to get the setup time */
for (dly = FIRST_DQ_DELAY; dly < MAX_DQDLY_TAPS; dly++) {
set_dly_factor(channel, STAGE_SETUP, type, dly);
err_value = dram_k_perbit(channel);
/* check fail bit, 0 ok, others fail */
for (bit = 0; bit < DATA_WIDTH_32BIT; bit++) {
fail_bit = err_value & ((u32)1 << bit);
dramk_check_dq_win(&(dqdqs_perbit_dly[bit]), dly,
MAX_DQDLY_TAPS - 1, fail_bit);
if (fail_bit == 0) {
dramc_dbg_msg("o");
} else {
dramc_dbg_msg("x");
}
}
dramc_dbg_msg("\n");
}
/* 2. set DQ delay to 0 */
set_dly_factor(channel, STAGE_SETUP, type, FIRST_DQ_DELAY);
/* DQS delay taps: tx and rx are 16 and 64 taps */
max_dqs_taps = (type == TX_WIN)? MAX_TX_DQSDLY_TAPS: MAX_RX_DQSDLY_TAPS;
dramc_dbg_msg("-----------------------------------"
"-------------------\n");
dramc_dbg_msg("Start DQS delay to find pass range,"
"DQ delay fixed to %#x...\n", FIRST_DQ_DELAY);
dramc_dbg_msg("------------------------------------"
"------------------\n");
dramc_dbg_msg("x-axis is bit #; y-axis is DQS delay (%d~%d)\n",
FIRST_DQS_DELAY + 1, max_dqs_taps - 1);
/* delay DQS to get the hold time, dq_dly = dqs_dly = 0 is counted */
/* when we delay dq, so we set first dqs delay to 1 */
for (dly = (FIRST_DQS_DELAY + 1); dly < max_dqs_taps; dly++) {
set_dly_factor(channel, STAGE_HOLD, type, dly);
err_value = dram_k_perbit(channel);
/* check fail bit, 0 ok, others fail */
for (bit = 0; bit < DATA_WIDTH_32BIT; bit++) {
fail_bit = err_value & ((u32)1 << bit);
dramk_check_dqs_win(&(dqdqs_perbit_dly[bit]), dly,
max_dqs_taps - 1, fail_bit);
if (fail_bit == 0) {
dramc_dbg_msg("o");
} else {
dramc_dbg_msg("x");
}
}
dramc_dbg_msg("\n");
}
/* 3 calculate dq and dqs time */
dramc_dbg_msg("-------------------------------"
"-----------------------\n");
dramc_dbg_msg("Start calculate dq time and dqs "
"time:\n");
dramc_dbg_msg("Find max DQS delay per byte / "
"Adjust DQ delay to align DQS...\n");
dramc_dbg_msg("--------------------------------"
"----------------------\n");
/* As per byte, check max DQS delay in 8-bit.
* Except for the bit of max DQS delay, delay
* DQ to fulfill setup time = hold time
*/
for (i = 0; i < DQS_NUMBER; i++) {
max_dqsdly_byte[i] = 0;
ave_dqdly_byte[i] = 0;
}
for (i = 0; i < DATA_WIDTH_32BIT; i++) {
/* we delay DQ or DQS to let DQS sample the middle */
/* of tx/rx pass window for all the 8 bits */
index = i / DQS_BIT_NUMBER;
fail |= dramk_calcu_best_dly(i, &dqdqs_perbit_dly[i],
&max_dqsdly_byte[index]);
if ((i + 1) % DQS_BIT_NUMBER == 0)
dramc_dbg_msg("----separate line----\n");
}
for (i = 0; i < DATA_WIDTH_32BIT; i++) {
/* dqs index for every 8-bit */
index = i / DQS_BIT_NUMBER;
/* set DQS to max for 8-bit */
if (dqdqs_perbit_dly[i].best_dqsdly < max_dqsdly_byte[index]) {
/* delay DQ to compensate extra DQS delay */
dly = max_dqsdly_byte[index] -
dqdqs_perbit_dly[i].best_dqsdly;
dqdqs_perbit_dly[i].best_dqdly += dly;
/* max limit to 15 */
max_limit = MAX_DQDLY_TAPS - 1;
if (dqdqs_perbit_dly[i].best_dqdly > max_limit) {
dqdqs_perbit_dly[i].best_dqdly = max_limit;
}
}
/* accumulation variable for TX DQM */
ave_dqdly_byte[index] += dqdqs_perbit_dly[i].best_dqdly;
/* take the average of DQ for TX DQM */
if ((i + 1) % DQS_BIT_NUMBER == 0) {
ave_dqdly_byte[index] /= DQS_BIT_NUMBER;
}
}
if (fail == 1) /* error handling */
die("fail on perbit_window_cal()\n");
dramc_dbg_msg("==================================================\n");
dramc_dbg_msg(" dramc_perbit_window_swcal:\n");
dramc_dbg_msg(" channel=%d(0:cha, 1:chb)\n", channel);
dramc_dbg_msg(" bus width=%d\n", DATA_WIDTH_32BIT);
dramc_dbg_msg("==================================================\n");
dramc_dbg_msg("DQS Delay :\n DQS0 = %d DQS1 = %d DQS2 = %d DQS3 = %d\n",
max_dqsdly_byte[0], max_dqsdly_byte[1],
max_dqsdly_byte[2], max_dqsdly_byte[3]);
if (type == TX_WIN)
dramc_dbg_msg("DQM Delay :\n"
"DQM0 = %d DQM1 = %d DQM2 = %d DQM3 = %d\n",
ave_dqdly_byte[0], ave_dqdly_byte[1],
ave_dqdly_byte[2], ave_dqdly_byte[3]);
dramc_dbg_msg("DQ Delay :\n");
for (i = 0; i < DATA_WIDTH_32BIT; i++) {
dramc_dbg_msg("DQ%d = %d ", i, dqdqs_perbit_dly[i].best_dqdly);
if ( ((i + 1) % 4) == 0)
dramc_dbg_msg("\n");
}
dramc_dbg_msg("____________________________________"
"____________________________________\n");
if (type == TX_WIN) {
/* Add CLK to DQS/DQ skew after write leveling */
dramc_dbg_msg("Add CLK to DQS/DQ skew based on write leveling.\n");
/* this subroutine add clk delay to DQS/DQ after WL */
tx_delay_for_wrleveling(channel, dqdqs_perbit_dly,
max_dqsdly_byte, ave_dqdly_byte);
}
if (type == TX_WIN)
set_tx_best_dly_factor(channel, dqdqs_perbit_dly,
max_dqsdly_byte, ave_dqdly_byte);
else
set_rx_best_dly_factor(channel, dqdqs_perbit_dly,
max_dqsdly_byte);
dramc_phy_reset(channel);
}