u-boot/drivers/ddr/marvell/axp/ddr3_pbs.c

1593 lines
43 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
/*
* Copyright (C) Marvell International Ltd. and its affiliates
*/
#include <common.h>
#include <i2c.h>
#include <spl.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#include <linux/delay.h>
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
#include "ddr3_hw_training.h"
/*
* Debug
*/
#define DEBUG_PBS_FULL_C(s, d, l) \
DEBUG_PBS_FULL_S(s); DEBUG_PBS_FULL_D(d, l); DEBUG_PBS_FULL_S("\n")
#define DEBUG_PBS_C(s, d, l) \
DEBUG_PBS_S(s); DEBUG_PBS_D(d, l); DEBUG_PBS_S("\n")
#ifdef MV_DEBUG_PBS
#define DEBUG_PBS_S(s) puts(s)
#define DEBUG_PBS_D(d, l) printf("%x", d)
#else
#define DEBUG_PBS_S(s)
#define DEBUG_PBS_D(d, l)
#endif
#ifdef MV_DEBUG_FULL_PBS
#define DEBUG_PBS_FULL_S(s) puts(s)
#define DEBUG_PBS_FULL_D(d, l) printf("%x", d)
#else
#define DEBUG_PBS_FULL_S(s)
#define DEBUG_PBS_FULL_D(d, l)
#endif
#if defined(MV88F78X60) || defined(MV88F672X)
/* Temp array for skew data storage */
static u32 skew_array[(MAX_PUP_NUM) * DQ_NUM] = { 0 };
/* PBS locked dq (per pup) */
extern u32 pbs_locked_dq[MAX_PUP_NUM][DQ_NUM];
extern u32 pbs_locked_dm[MAX_PUP_NUM];
extern u32 pbs_locked_value[MAX_PUP_NUM][DQ_NUM];
#if defined(MV88F672X)
extern u32 pbs_pattern[2][LEN_16BIT_PBS_PATTERN];
extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN];
#else
extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN];
extern u32 pbs_pattern_64b[2][LEN_PBS_PATTERN];
#endif
extern u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM];
static int ddr3_tx_shift_dqs_adll_step_before_fail(MV_DRAM_INFO *dram_info,
u32 cur_pup, u32 pbs_pattern_idx, u32 ecc);
static int ddr3_rx_shift_dqs_to_first_fail(MV_DRAM_INFO *dram_info, u32 cur_pup,
u32 pbs_pattern_idx, u32 ecc);
static int ddr3_pbs_per_bit(MV_DRAM_INFO *dram_info, int *start_over, int is_tx,
u32 *pcur_pup, u32 pbs_pattern_idx, u32 ecc);
static int ddr3_set_pbs_results(MV_DRAM_INFO *dram_info, int is_tx);
static void ddr3_pbs_write_pup_dqs_reg(u32 cs, u32 pup, u32 dqs_delay);
/*
* Name: ddr3_pbs_tx
* Desc: Execute the PBS TX phase.
* Args: dram_info ddr3 training information struct
* Notes:
* Returns: MV_OK if success, other error code if fail.
*/
int ddr3_pbs_tx(MV_DRAM_INFO *dram_info)
{
/* Array of Deskew results */
/*
* Array to hold the total sum of skew from all iterations
* (for average purpose)
*/
u32 skew_sum_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
/*
* Array to hold the total average skew from both patterns
* (for average purpose)
*/
u32 pattern_skew_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
u32 pbs_rep_time = 0; /* counts number of loop in case of fail */
/* bit array for unlock pups - used to repeat on the RX operation */
u32 cur_pup;
u32 max_pup;
u32 pbs_retry;
u32 pup, dq, pups, cur_max_pup, valid_pup, reg;
u32 pattern_idx;
u32 ecc;
/* indicates whether we need to start the loop again */
int start_over;
DEBUG_PBS_S("DDR3 - PBS TX - Starting PBS TX procedure\n");
pups = dram_info->num_of_total_pups;
max_pup = dram_info->num_of_total_pups;
/* Enable SW override */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
/* [0] = 1 - Enable SW override */
/* 0x15B8 - Training SW 2 Register */
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
DEBUG_PBS_S("DDR3 - PBS RX - SW Override Enabled\n");
reg = 1 << REG_DRAM_TRAINING_AUTO_OFFS;
reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */
/* Running twice for 2 different patterns. each patterns - 3 times */
for (pattern_idx = 0; pattern_idx < COUNT_PBS_PATTERN; pattern_idx++) {
DEBUG_PBS_C("DDR3 - PBS TX - Working with pattern - ",
pattern_idx, 1);
/* Reset sum array */
for (pup = 0; pup < pups; pup++) {
for (dq = 0; dq < DQ_NUM; dq++)
skew_sum_array[pup][dq] = 0;
}
/*
* Perform PBS several of times (3 for each pattern).
* At the end, we'll use the average
*/
/* If there is ECC, do each PBS again with mux change */
for (pbs_retry = 0; pbs_retry < COUNT_PBS_REPEAT; pbs_retry++) {
for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) {
/*
* This parameter stores the current PUP
* num - ecc mode dependent - 4-8 / 1 pups
*/
cur_max_pup = (1 - ecc) *
dram_info->num_of_std_pups + ecc;
if (ecc) {
/* Only 1 pup in this case */
valid_pup = 0x1;
} else if (cur_max_pup > 4) {
/* 64 bit - 8 pups */
valid_pup = 0xFF;
} else if (cur_max_pup == 4) {
/* 32 bit - 4 pups */
valid_pup = 0xF;
} else {
/* 16 bit - 2 pups */
valid_pup = 0x3;
}
/* ECC Support - Switch ECC Mux on ecc=1 */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
reg |= (dram_info->ecc_ena * ecc <<
REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
if (ecc)
DEBUG_PBS_S("DDR3 - PBS Tx - ECC Mux Enabled\n");
else
DEBUG_PBS_S("DDR3 - PBS Tx - ECC Mux Disabled\n");
/* Init iteration values */
/* Clear the locked DQs */
for (pup = 0; pup < cur_max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++) {
pbs_locked_dq[
pup + ecc *
(max_pup - 1)][dq] =
0;
}
}
pbs_rep_time = 0;
cur_pup = valid_pup;
start_over = 0;
/*
* Run loop On current Pattern and current
* pattern iteration (just to cover the false
* fail problem)
*/
do {
DEBUG_PBS_S("DDR3 - PBS Tx - Pbs Rep Loop is ");
DEBUG_PBS_D(pbs_rep_time, 1);
DEBUG_PBS_S(", for Retry No.");
DEBUG_PBS_D(pbs_retry, 1);
DEBUG_PBS_S("\n");
/* Set all PBS values to MIN (0) */
DEBUG_PBS_S("DDR3 - PBS Tx - Set all PBS values to MIN\n");
for (dq = 0; dq < DQ_NUM; dq++) {
ddr3_write_pup_reg(
PUP_PBS_TX +
pbs_dq_mapping[pup *
(1 - ecc) +
ecc * ECC_PUP]
[dq], CS0, (1 - ecc) *
PUP_BC + ecc * ECC_PUP, 0,
0);
}
/*
* Shift DQ ADLL right, One step before
* fail
*/
DEBUG_PBS_S("DDR3 - PBS Tx - ADLL shift right one phase before fail\n");
if (MV_OK != ddr3_tx_shift_dqs_adll_step_before_fail
(dram_info, cur_pup, pattern_idx,
ecc))
return MV_DDR3_TRAINING_ERR_PBS_ADLL_SHR_1PHASE;
/* PBS For each bit */
DEBUG_PBS_S("DDR3 - PBS Tx - perform PBS for each bit\n");
/*
* In this stage - start_over = 0
*/
if (MV_OK != ddr3_pbs_per_bit(
dram_info, &start_over, 1,
&cur_pup, pattern_idx, ecc))
return MV_DDR3_TRAINING_ERR_PBS_TX_PER_BIT;
} while ((start_over == 1) &&
(++pbs_rep_time < COUNT_PBS_STARTOVER));
if (pbs_rep_time == COUNT_PBS_STARTOVER &&
start_over == 1) {
DEBUG_PBS_S("DDR3 - PBS Tx - FAIL - Adll reach max value\n");
return MV_DDR3_TRAINING_ERR_PBS_TX_MAX_VAL;
}
DEBUG_PBS_FULL_C("DDR3 - PBS TX - values for iteration - ",
pbs_retry, 1);
for (pup = 0; pup < cur_max_pup; pup++) {
/*
* To minimize delay elements, inc
* from pbs value the min pbs val
*/
DEBUG_PBS_S("DDR3 - PBS - PUP");
DEBUG_PBS_D((pup + (ecc * ECC_PUP)), 1);
DEBUG_PBS_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
/* Set skew value for all dq */
/*
* Bit# Deskew <- Bit# Deskew -
* last / first failing bit
* Deskew For all bits (per PUP)
* (minimize delay elements)
*/
DEBUG_PBS_S("DQ");
DEBUG_PBS_D(dq, 1);
DEBUG_PBS_S("-");
DEBUG_PBS_D(skew_array
[((pup) * DQ_NUM) +
dq], 2);
DEBUG_PBS_S(", ");
}
DEBUG_PBS_S("\n");
}
/*
* Collect the results we got on this trial
* of PBS
*/
for (pup = 0; pup < cur_max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++) {
skew_sum_array[pup + (ecc * (max_pup - 1))]
[dq] += skew_array
[((pup) * DQ_NUM) + dq];
}
}
/* ECC Support - Disable ECC MUX */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
}
}
DEBUG_PBS_C("DDR3 - PBS TX - values for current pattern - ",
pattern_idx, 1);
for (pup = 0; pup < max_pup; pup++) {
/*
* To minimize delay elements, inc from pbs value the
* min pbs val
*/
DEBUG_PBS_S("DDR3 - PBS - PUP");
DEBUG_PBS_D(pup, 1);
DEBUG_PBS_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
/* set skew value for all dq */
/* Bit# Deskew <- Bit# Deskew - last / first failing bit Deskew For all bits (per PUP) (minimize delay elements) */
DEBUG_PBS_S("DQ");
DEBUG_PBS_D(dq, 1);
DEBUG_PBS_S("-");
DEBUG_PBS_D(skew_sum_array[pup][dq] /
COUNT_PBS_REPEAT, 2);
DEBUG_PBS_S(", ");
}
DEBUG_PBS_S("\n");
}
/*
* Calculate the average skew for current pattern for each
* pup and each bit
*/
DEBUG_PBS_C("DDR3 - PBS TX - Average for pattern - ",
pattern_idx, 1);
for (pup = 0; pup < max_pup; pup++) {
/*
* FOR ECC only :: found min and max value for current
* pattern skew array
*/
/* Loop for all dqs */
for (dq = 0; dq < DQ_NUM; dq++) {
pattern_skew_array[pup][dq] +=
(skew_sum_array[pup][dq] /
COUNT_PBS_REPEAT);
}
}
}
/* Calculate the average skew */
for (pup = 0; pup < max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++)
skew_array[((pup) * DQ_NUM) + dq] =
pattern_skew_array[pup][dq] / COUNT_PBS_PATTERN;
}
DEBUG_PBS_S("DDR3 - PBS TX - Average for all patterns:\n");
for (pup = 0; pup < max_pup; pup++) {
/*
* To minimize delay elements, inc from pbs value the min
* pbs val
*/
DEBUG_PBS_S("DDR3 - PBS - PUP");
DEBUG_PBS_D(pup, 1);
DEBUG_PBS_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
/* Set skew value for all dq */
/*
* Bit# Deskew <- Bit# Deskew - last / first
* failing bit Deskew For all bits (per PUP)
* (minimize delay elements)
*/
DEBUG_PBS_S("DQ");
DEBUG_PBS_D(dq, 1);
DEBUG_PBS_S("-");
DEBUG_PBS_D(skew_array[(pup * DQ_NUM) + dq], 2);
DEBUG_PBS_S(", ");
}
DEBUG_PBS_S("\n");
}
/* Return ADLL to default value */
for (pup = 0; pup < max_pup; pup++) {
if (pup == (max_pup - 1) && dram_info->ecc_ena)
pup = ECC_PUP;
ddr3_pbs_write_pup_dqs_reg(CS0, pup, INIT_WL_DELAY);
}
/* Set averaged PBS results */
ddr3_set_pbs_results(dram_info, 1);
/* Disable SW override - Must be in a different stage */
/* [0]=0 - Enable SW override */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
/* 0x15B8 - Training SW 2 Register */
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
DEBUG_PBS_S("DDR3 - PBS Tx - PBS TX ended successfuly\n");
return MV_OK;
}
/*
* Name: ddr3_tx_shift_dqs_adll_step_before_fail
* Desc: Execute the Tx shift DQ phase.
* Args: dram_info ddr3 training information struct
* cur_pup bit array of the function active pups.
* pbs_pattern_idx Index of PBS pattern
* Notes:
* Returns: MV_OK if success, other error code if fail.
*/
static int ddr3_tx_shift_dqs_adll_step_before_fail(MV_DRAM_INFO *dram_info,
u32 cur_pup,
u32 pbs_pattern_idx, u32 ecc)
{
u32 unlock_pup; /* bit array of unlock pups */
u32 new_lockup_pup; /* bit array of compare failed pups */
u32 adll_val = 4; /* INIT_WL_DELAY */
u32 cur_max_pup, pup;
u32 dqs_dly_set[MAX_PUP_NUM] = { 0 };
u32 *pattern_ptr;
/* Choose pattern */
switch (dram_info->ddr_width) {
#if defined(MV88F672X)
case 16:
pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx];
break;
#endif
case 32:
pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx];
break;
#if defined(MV88F78X60)
case 64:
pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx];
break;
#endif
default:
return MV_FAIL;
}
/* Set current pup number */
if (cur_pup == 0x1) /* Ecc mode */
cur_max_pup = 1;
else
cur_max_pup = dram_info->num_of_std_pups;
unlock_pup = cur_pup; /* '1' for each unlocked pup */
/* Loop on all ADLL Vaules */
do {
/* Loop until found first fail */
adll_val++;
/*
* Increment (Move to right - ADLL) DQ TX delay
* (broadcast to all Data PUPs)
*/
for (pup = 0; pup < cur_max_pup; pup++)
ddr3_pbs_write_pup_dqs_reg(CS0,
pup * (1 - ecc) +
ECC_PUP * ecc, adll_val);
/*
* Write and Read, compare results (read was already verified)
*/
/* 0 - all locked */
new_lockup_pup = 0;
if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup,
&new_lockup_pup,
pattern_ptr, LEN_PBS_PATTERN,
SDRAM_PBS_TX_OFFS, 1, 0,
NULL,
0))
return MV_FAIL;
unlock_pup &= ~new_lockup_pup;
DEBUG_PBS_FULL_S("Shift DQS by 2 steps for PUPs: ");
DEBUG_PBS_FULL_D(unlock_pup, 2);
DEBUG_PBS_FULL_C(", Set ADLL value = ", adll_val, 2);
/* If any PUP failed there is '1' to mark the PUP */
if (new_lockup_pup != 0) {
/*
* Decrement (Move Back to Left two steps - ADLL)
* DQ TX delay for current failed pups and save
*/
for (pup = 0; pup < cur_max_pup; pup++) {
if (((new_lockup_pup >> pup) & 0x1) &&
dqs_dly_set[pup] == 0)
dqs_dly_set[pup] = adll_val - 1;
}
}
} while ((unlock_pup != 0) && (adll_val != ADLL_MAX));
if (unlock_pup != 0) {
DEBUG_PBS_FULL_S("DDR3 - PBS Tx - Shift DQ - Adll value reached maximum\n");
for (pup = 0; pup < cur_max_pup; pup++) {
if (((unlock_pup >> pup) & 0x1) &&
dqs_dly_set[pup] == 0)
dqs_dly_set[pup] = adll_val - 1;
}
}
DEBUG_PBS_FULL_C("PBS TX one step before fail last pups locked Adll ",
adll_val - 2, 2);
/* Set the PUP DQS DLY Values */
for (pup = 0; pup < cur_max_pup; pup++)
ddr3_pbs_write_pup_dqs_reg(CS0, pup * (1 - ecc) + ECC_PUP * ecc,
dqs_dly_set[pup]);
/* Found one phase before fail */
return MV_OK;
}
/*
* Name: ddr3_pbs_rx
* Desc: Execute the PBS RX phase.
* Args: dram_info ddr3 training information struct
* Notes:
* Returns: MV_OK if success, other error code if fail.
*/
int ddr3_pbs_rx(MV_DRAM_INFO *dram_info)
{
/*
* Array to hold the total sum of skew from all iterations
* (for average purpose)
*/
u32 skew_sum_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
/*
* Array to hold the total average skew from both patterns
* (for average purpose)
*/
u32 pattern_skew_array[MAX_PUP_NUM][DQ_NUM] = { {0} };
u32 pbs_rep_time = 0; /* counts number of loop in case of fail */
/* bit array for unlock pups - used to repeat on the RX operation */
u32 cur_pup;
u32 max_pup;
u32 pbs_retry;
u32 pup, dq, pups, cur_max_pup, valid_pup, reg;
u32 pattern_idx;
u32 ecc;
/* indicates whether we need to start the loop again */
int start_over;
int status;
DEBUG_PBS_S("DDR3 - PBS RX - Starting PBS RX procedure\n");
pups = dram_info->num_of_total_pups;
max_pup = dram_info->num_of_total_pups;
/* Enable SW override */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR) |
(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
/* [0] = 1 - Enable SW override */
/* 0x15B8 - Training SW 2 Register */
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
DEBUG_PBS_FULL_S("DDR3 - PBS RX - SW Override Enabled\n");
reg = 1 << REG_DRAM_TRAINING_AUTO_OFFS;
reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */
/* Running twice for 2 different patterns. each patterns - 3 times */
for (pattern_idx = 0; pattern_idx < COUNT_PBS_PATTERN; pattern_idx++) {
DEBUG_PBS_FULL_C("DDR3 - PBS RX - Working with pattern - ",
pattern_idx, 1);
/* Reset sum array */
for (pup = 0; pup < pups; pup++) {
for (dq = 0; dq < DQ_NUM; dq++)
skew_sum_array[pup][dq] = 0;
}
/*
* Perform PBS several of times (3 for each pattern).
* At the end, we'll use the average
*/
/* If there is ECC, do each PBS again with mux change */
for (pbs_retry = 0; pbs_retry < COUNT_PBS_REPEAT; pbs_retry++) {
for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) {
/*
* This parameter stores the current PUP
* num - ecc mode dependent - 4-8 / 1 pups
*/
cur_max_pup = (1 - ecc) *
dram_info->num_of_std_pups + ecc;
if (ecc) {
/* Only 1 pup in this case */
valid_pup = 0x1;
} else if (cur_max_pup > 4) {
/* 64 bit - 8 pups */
valid_pup = 0xFF;
} else if (cur_max_pup == 4) {
/* 32 bit - 4 pups */
valid_pup = 0xF;
} else {
/* 16 bit - 2 pups */
valid_pup = 0x3;
}
/* ECC Support - Switch ECC Mux on ecc=1 */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
reg |= (dram_info->ecc_ena * ecc <<
REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
if (ecc)
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - ECC Mux Enabled\n");
else
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - ECC Mux Disabled\n");
/* Init iteration values */
/* Clear the locked DQs */
for (pup = 0; pup < cur_max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++) {
pbs_locked_dq[
pup + ecc * (max_pup - 1)][dq] =
0;
}
}
pbs_rep_time = 0;
cur_pup = valid_pup;
start_over = 0;
/*
* Run loop On current Pattern and current
* pattern iteration (just to cover the false
* fail problem
*/
do {
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Pbs Rep Loop is ");
DEBUG_PBS_FULL_D(pbs_rep_time, 1);
DEBUG_PBS_FULL_S(", for Retry No.");
DEBUG_PBS_FULL_D(pbs_retry, 1);
DEBUG_PBS_FULL_S("\n");
/* Set all PBS values to MAX (31) */
for (pup = 0; pup < cur_max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++)
ddr3_write_pup_reg(
PUP_PBS_RX +
pbs_dq_mapping[
pup * (1 - ecc)
+ ecc * ECC_PUP]
[dq], CS0,
pup + ecc * ECC_PUP,
0, MAX_PBS);
}
/* Set all DQS PBS values to MIN (0) */
for (pup = 0; pup < cur_max_pup; pup++) {
ddr3_write_pup_reg(PUP_PBS_RX +
DQ_NUM, CS0,
pup +
ecc *
ECC_PUP, 0,
0);
}
/* Shift DQS, To first Fail */
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Shift RX DQS to first fail\n");
status = ddr3_rx_shift_dqs_to_first_fail
(dram_info, cur_pup,
pattern_idx, ecc);
if (MV_OK != status) {
DEBUG_PBS_S("DDR3 - PBS Rx - ddr3_rx_shift_dqs_to_first_fail failed.\n");
DEBUG_PBS_D(status, 8);
DEBUG_PBS_S("\nDDR3 - PBS Rx - SKIP.\n");
/* Reset read FIFO */
reg = reg_read(REG_DRAM_TRAINING_ADDR);
/* Start Auto Read Leveling procedure */
reg |= (1 << REG_DRAM_TRAINING_RL_OFFS);
/* 0x15B0 - Training Register */
reg_write(REG_DRAM_TRAINING_ADDR, reg);
reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS)
+ (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS));
/* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset */
/* 0x15B8 - Training SW 2 Register */
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
do {
reg = (reg_read(REG_DRAM_TRAINING_2_ADDR))
& (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
} while (reg); /* Wait for '0' */
reg = reg_read(REG_DRAM_TRAINING_ADDR);
/* Clear Auto Read Leveling procedure */
reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS);
/* 0x15B0 - Training Register */
reg_write(REG_DRAM_TRAINING_ADDR, reg);
/* Set ADLL to 15 */
for (pup = 0; pup < max_pup;
pup++) {
ddr3_write_pup_reg
(PUP_DQS_RD, CS0,
pup +
(ecc * ECC_PUP), 0,
15);
}
/* Set all PBS values to MIN (0) */
for (pup = 0; pup < cur_max_pup;
pup++) {
for (dq = 0;
dq < DQ_NUM; dq++)
ddr3_write_pup_reg
(PUP_PBS_RX +
pbs_dq_mapping
[pup * (1 - ecc) +
ecc * ECC_PUP]
[dq], CS0,
pup + ecc * ECC_PUP,
0, MIN_PBS);
}
return MV_OK;
}
/* PBS For each bit */
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - perform PBS for each bit\n");
/* in this stage - start_over = 0; */
if (MV_OK != ddr3_pbs_per_bit(
dram_info, &start_over,
0, &cur_pup,
pattern_idx, ecc)) {
DEBUG_PBS_S("DDR3 - PBS Rx - ddr3_pbs_per_bit failed.");
return MV_DDR3_TRAINING_ERR_PBS_RX_PER_BIT;
}
} while ((start_over == 1) &&
(++pbs_rep_time < COUNT_PBS_STARTOVER));
if (pbs_rep_time == COUNT_PBS_STARTOVER &&
start_over == 1) {
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - FAIL - Algorithm failed doing RX PBS\n");
return MV_DDR3_TRAINING_ERR_PBS_RX_MAX_VAL;
}
/* Return DQS ADLL to default value - 15 */
/* Set all DQS PBS values to MIN (0) */
for (pup = 0; pup < cur_max_pup; pup++)
ddr3_write_pup_reg(PUP_DQS_RD, CS0,
pup + ecc * ECC_PUP,
0, INIT_RL_DELAY);
DEBUG_PBS_FULL_C("DDR3 - PBS RX - values for iteration - ",
pbs_retry, 1);
for (pup = 0; pup < cur_max_pup; pup++) {
/*
* To minimize delay elements, inc from
* pbs value the min pbs val
*/
DEBUG_PBS_FULL_S("DDR3 - PBS - PUP");
DEBUG_PBS_FULL_D((pup +
(ecc * ECC_PUP)), 1);
DEBUG_PBS_FULL_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
/* Set skew value for all dq */
/*
* Bit# Deskew <- Bit# Deskew -
* last / first failing bit
* Deskew For all bits (per PUP)
* (minimize delay elements)
*/
DEBUG_PBS_FULL_S("DQ");
DEBUG_PBS_FULL_D(dq, 1);
DEBUG_PBS_FULL_S("-");
DEBUG_PBS_FULL_D(skew_array
[((pup) *
DQ_NUM) +
dq], 2);
DEBUG_PBS_FULL_S(", ");
}
DEBUG_PBS_FULL_S("\n");
}
/*
* Collect the results we got on this trial
* of PBS
*/
for (pup = 0; pup < cur_max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++) {
skew_sum_array
[pup + (ecc * (max_pup - 1))]
[dq] +=
skew_array[((pup) * DQ_NUM) + dq];
}
}
/* ECC Support - Disable ECC MUX */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS);
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
}
}
/*
* Calculate the average skew for current pattern for each
* pup and each bit
*/
DEBUG_PBS_FULL_C("DDR3 - PBS RX - Average for pattern - ",
pattern_idx, 1);
for (pup = 0; pup < max_pup; pup++) {
/*
* FOR ECC only :: found min and max value for
* current pattern skew array
*/
/* Loop for all dqs */
for (dq = 0; dq < DQ_NUM; dq++) {
pattern_skew_array[pup][dq] +=
(skew_sum_array[pup][dq] /
COUNT_PBS_REPEAT);
}
}
DEBUG_PBS_C("DDR3 - PBS RX - values for current pattern - ",
pattern_idx, 1);
for (pup = 0; pup < max_pup; pup++) {
/*
* To minimize delay elements, inc from pbs value the
* min pbs val
*/
DEBUG_PBS_S("DDR3 - PBS RX - PUP");
DEBUG_PBS_D(pup, 1);
DEBUG_PBS_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
/* Set skew value for all dq */
/*
* Bit# Deskew <- Bit# Deskew - last / first
* failing bit Deskew For all bits (per PUP)
* (minimize delay elements)
*/
DEBUG_PBS_S("DQ");
DEBUG_PBS_D(dq, 1);
DEBUG_PBS_S("-");
DEBUG_PBS_D(skew_sum_array[pup][dq] /
COUNT_PBS_REPEAT, 2);
DEBUG_PBS_S(", ");
}
DEBUG_PBS_S("\n");
}
}
/* Calculate the average skew */
for (pup = 0; pup < max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++)
skew_array[((pup) * DQ_NUM) + dq] =
pattern_skew_array[pup][dq] / COUNT_PBS_PATTERN;
}
DEBUG_PBS_S("DDR3 - PBS RX - Average for all patterns:\n");
for (pup = 0; pup < max_pup; pup++) {
/*
* To minimize delay elements, inc from pbs value the
* min pbs val
*/
DEBUG_PBS_S("DDR3 - PBS - PUP");
DEBUG_PBS_D(pup, 1);
DEBUG_PBS_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
/* Set skew value for all dq */
/*
* Bit# Deskew <- Bit# Deskew - last / first
* failing bit Deskew For all bits (per PUP)
* (minimize delay elements)
*/
DEBUG_PBS_S("DQ");
DEBUG_PBS_D(dq, 1);
DEBUG_PBS_S("-");
DEBUG_PBS_D(skew_array[(pup * DQ_NUM) + dq], 2);
DEBUG_PBS_S(", ");
}
DEBUG_PBS_S("\n");
}
/* Return ADLL to default value */
ddr3_write_pup_reg(PUP_DQS_RD, CS0, PUP_BC, 0, INIT_RL_DELAY);
/* Set averaged PBS results */
ddr3_set_pbs_results(dram_info, 0);
/* Disable SW override - Must be in a different stage */
/* [0]=0 - Enable SW override */
reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS);
/* 0x15B8 - Training SW 2 Register */
reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
reg = reg_read(REG_DRAM_TRAINING_1_ADDR) |
(1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS);
reg_write(REG_DRAM_TRAINING_1_ADDR, reg);
DEBUG_PBS_FULL_S("DDR3 - PBS RX - ended successfuly\n");
return MV_OK;
}
/*
* Name: ddr3_rx_shift_dqs_to_first_fail
* Desc: Execute the Rx shift DQ phase.
* Args: dram_info ddr3 training information struct
* cur_pup bit array of the function active pups.
* pbs_pattern_idx Index of PBS pattern
* Notes:
* Returns: MV_OK if success, other error code if fail.
*/
static int ddr3_rx_shift_dqs_to_first_fail(MV_DRAM_INFO *dram_info, u32 cur_pup,
u32 pbs_pattern_idx, u32 ecc)
{
u32 unlock_pup; /* bit array of unlock pups */
u32 new_lockup_pup; /* bit array of compare failed pups */
u32 adll_val = MAX_DELAY;
u32 dqs_deskew_val = 0; /* current value of DQS PBS deskew */
u32 cur_max_pup, pup, pass_pup;
u32 *pattern_ptr;
/* Choose pattern */
switch (dram_info->ddr_width) {
#if defined(MV88F672X)
case 16:
pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx];
break;
#endif
case 32:
pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx];
break;
#if defined(MV88F78X60)
case 64:
pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx];
break;
#endif
default:
return MV_FAIL;
}
/* Set current pup number */
if (cur_pup == 0x1) /* Ecc mode */
cur_max_pup = 1;
else
cur_max_pup = dram_info->num_of_std_pups;
unlock_pup = cur_pup; /* '1' for each unlocked pup */
DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Starting...\n");
/* Set DQS ADLL to MAX */
DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Set DQS ADLL to Max for all PUPs\n");
for (pup = 0; pup < cur_max_pup; pup++)
ddr3_write_pup_reg(PUP_DQS_RD, CS0, pup + ecc * ECC_PUP, 0,
MAX_DELAY);
/* Loop on all ADLL Vaules */
do {
/* Loop until found fail for all pups */
new_lockup_pup = 0;
if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup,
&new_lockup_pup,
pattern_ptr, LEN_PBS_PATTERN,
SDRAM_PBS_I_OFFS +
pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS,
0, 0, NULL, 0)) {
DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP(ddr3_sdram_compare)\n");
return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP;
}
if ((new_lockup_pup != 0) && (dqs_deskew_val <= 1)) {
/* Fail on start with first deskew value */
/* Decrement DQS ADLL */
--adll_val;
if (adll_val == ADLL_MIN) {
DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - fail on start with first deskew value\n");
return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP;
}
ddr3_write_pup_reg(PUP_DQS_RD, CS0, pup + ecc * ECC_PUP,
0, adll_val);
continue;
}
/* Update all new locked pups */
unlock_pup &= ~new_lockup_pup;
if ((unlock_pup == 0) || (dqs_deskew_val == MAX_PBS)) {
if (dqs_deskew_val == MAX_PBS) {
/*
* Reach max value of dqs deskew or get fail
* for all pups
*/
DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - DQS deskew reached maximum value\n");
}
break;
}
DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Inc DQS deskew for PUPs: ");
DEBUG_PBS_FULL_D(unlock_pup, 2);
DEBUG_PBS_FULL_C(", deskew = ", dqs_deskew_val, 2);
/* Increment DQS deskew elements - Only for unlocked pups */
dqs_deskew_val++;
for (pup = 0; pup < cur_max_pup; pup++) {
if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
ddr3_write_pup_reg(PUP_PBS_RX + DQS_DQ_NUM, CS0,
pup + ecc * ECC_PUP, 0,
dqs_deskew_val);
}
}
} while (1);
DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - ADLL shift one step before fail\n");
/* Continue to ADLL shift one step before fail */
unlock_pup = cur_pup;
do {
/* Loop until pass compare for all pups */
new_lockup_pup = 0;
/* Read and compare results */
if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup, &new_lockup_pup,
pattern_ptr, LEN_PBS_PATTERN,
SDRAM_PBS_I_OFFS +
pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS,
1, 0, NULL, 0)) {
DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP(ddr3_sdram_compare)\n");
return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP;
}
/*
* Get mask for pup which passed so their adll will be
* changed to 2 steps before fails
*/
pass_pup = unlock_pup & ~new_lockup_pup;
DEBUG_PBS_FULL_S("Shift DQS by 2 steps for PUPs: ");
DEBUG_PBS_FULL_D(pass_pup, 2);
DEBUG_PBS_FULL_C(", Set ADLL value = ", (adll_val - 2), 2);
/* Only for pass pups */
for (pup = 0; pup < cur_max_pup; pup++) {
if (IS_PUP_ACTIVE(pass_pup, pup) == 1) {
ddr3_write_pup_reg(PUP_DQS_RD, CS0,
pup + ecc * ECC_PUP, 0,
(adll_val - 2));
}
}
/* Locked pups that compare success */
unlock_pup &= new_lockup_pup;
if (unlock_pup == 0) {
/* All pups locked */
break;
}
/* Found error */
if (adll_val == 0) {
DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Shift DQS - Adll reach min value\n");
return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_MAX_VAL;
}
/*
* Decrement (Move Back to Left one phase - ADLL) dqs RX delay
*/
adll_val--;
for (pup = 0; pup < cur_max_pup; pup++) {
if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) {
ddr3_write_pup_reg(PUP_DQS_RD, CS0,
pup + ecc * ECC_PUP, 0,
adll_val);
}
}
} while (1);
return MV_OK;
}
/*
* lock_pups() extracted from ddr3_pbs_per_bit(). This just got too
* much indented making it hard to read / edit.
*/
static void lock_pups(u32 pup, u32 *pup_locked, u8 *unlock_pup_dq_array,
u32 pbs_curr_val, u32 start_pbs, u32 ecc, int is_tx)
{
u32 dq;
int idx;
/* Lock PBS value for all remaining PUPs bits */
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Lock PBS value for all remaining PUPs bits, pup ");
DEBUG_PBS_FULL_D(pup, 1);
DEBUG_PBS_FULL_C(" pbs value ", pbs_curr_val, 2);
idx = pup * (1 - ecc) + ecc * ECC_PUP;
*pup_locked &= ~(1 << pup);
for (dq = 0; dq < DQ_NUM; dq++) {
if (IS_PUP_ACTIVE(unlock_pup_dq_array[dq], pup) == 1) {
int offs;
/* Lock current dq */
unlock_pup_dq_array[dq] &= ~(1 << pup);
skew_array[(pup * DQ_NUM) + dq] = pbs_curr_val;
if (is_tx == 1)
offs = PUP_PBS_TX;
else
offs = PUP_PBS_RX;
ddr3_write_pup_reg(offs +
pbs_dq_mapping[idx][dq], CS0,
idx, 0, start_pbs);
}
}
}
/*
* Name: ddr3_pbs_per_bit
* Desc: Execute the Per Bit Skew phase.
* Args: start_over Return whether need to start over the algorithm
* is_tx Indicate whether Rx or Tx
* pcur_pup bit array of the function active pups. return the
* pups that need to repeat on the PBS
* pbs_pattern_idx Index of PBS pattern
*
* Notes: Current implementation supports double activation of this function.
* i.e. in order to activate this function (using start_over) more than
* twice, the implementation should change.
* imlementation limitation are marked using
* ' CHIP-ONLY! - Implementation Limitation '
* Returns: MV_OK if success, other error code if fail.
*/
static int ddr3_pbs_per_bit(MV_DRAM_INFO *dram_info, int *start_over, int is_tx,
u32 *pcur_pup, u32 pbs_pattern_idx, u32 ecc)
{
/*
* Bit array to indicate if we already get fail on bit per pup & dq bit
*/
u8 unlock_pup_dq_array[DQ_NUM] = {
*pcur_pup, *pcur_pup, *pcur_pup, *pcur_pup, *pcur_pup,
*pcur_pup, *pcur_pup, *pcur_pup
};
u8 cmp_unlock_pup_dq_array[COUNT_PBS_COMP_RETRY_NUM][DQ_NUM];
u32 pup, dq;
/* value of pbs is according to RX or TX */
u32 start_pbs, last_pbs;
u32 pbs_curr_val;
/* bit array that indicates all dq of the pup locked */
u32 pup_locked;
u32 first_fail[MAX_PUP_NUM] = { 0 }; /* count first fail per pup */
/* indicates whether we get first fail per pup */
int first_failed[MAX_PUP_NUM] = { 0 };
/* bit array that indicates pup already get fail */
u32 sum_pup_fail;
/* use to calculate diff between curr pbs to first fail pbs */
u32 calc_pbs_diff;
u32 pbs_cmp_retry;
u32 max_pup;
/* Set init values for retry array - 8 retry */
for (pbs_cmp_retry = 0; pbs_cmp_retry < COUNT_PBS_COMP_RETRY_NUM;
pbs_cmp_retry++) {
for (dq = 0; dq < DQ_NUM; dq++)
cmp_unlock_pup_dq_array[pbs_cmp_retry][dq] = *pcur_pup;
}
memset(&skew_array, 0, MAX_PUP_NUM * DQ_NUM * sizeof(u32));
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Started\n");
/* The pbs value depends if rx or tx */
if (is_tx == 1) {
start_pbs = MIN_PBS;
last_pbs = MAX_PBS;
} else {
start_pbs = MAX_PBS;
last_pbs = MIN_PBS;
}
pbs_curr_val = start_pbs;
pup_locked = *pcur_pup;
/* Set current pup number */
if (pup_locked == 0x1) /* Ecc mode */
max_pup = 1;
else
max_pup = dram_info->num_of_std_pups;
do {
/* Increment/ decrement PBS for un-lock bits only */
if (is_tx == 1)
pbs_curr_val++;
else
pbs_curr_val--;
/* Set Current PBS delay */
for (dq = 0; dq < DQ_NUM; dq++) {
/* Check DQ bits to see if locked in all pups */
if (unlock_pup_dq_array[dq] == 0) {
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - All pups are locked for DQ ");
DEBUG_PBS_FULL_D(dq, 1);
DEBUG_PBS_FULL_S("\n");
continue;
}
for (pup = 0; pup < max_pup; pup++) {
int idx;
idx = pup * (1 - ecc) + ecc * ECC_PUP;
if (IS_PUP_ACTIVE(unlock_pup_dq_array[dq], pup)
== 0)
continue;
if (is_tx == 1)
ddr3_write_pup_reg(
PUP_PBS_TX + pbs_dq_mapping[idx][dq],
CS0, idx, 0, pbs_curr_val);
else
ddr3_write_pup_reg(
PUP_PBS_RX + pbs_dq_mapping[idx][dq],
CS0, idx, 0, pbs_curr_val);
}
}
/*
* Write Read and compare results - run the test
* DDR_PBS_COMP_RETRY_NUM times
*/
/* Run number of read and write to verify */
for (pbs_cmp_retry = 0;
pbs_cmp_retry < COUNT_PBS_COMP_RETRY_NUM;
pbs_cmp_retry++) {
if (MV_OK !=
ddr3_sdram_pbs_compare(dram_info, pup_locked, is_tx,
pbs_pattern_idx,
pbs_curr_val, start_pbs,
skew_array,
cmp_unlock_pup_dq_array
[pbs_cmp_retry], ecc))
return MV_FAIL;
for (pup = 0; pup < max_pup; pup++) {
for (dq = 0; dq < DQ_NUM; dq++) {
if ((IS_PUP_ACTIVE(unlock_pup_dq_array[dq],
pup) == 1)
&& (IS_PUP_ACTIVE(cmp_unlock_pup_dq_array
[pbs_cmp_retry][dq],
pup) == 0)) {
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - PbsCurrVal: ");
DEBUG_PBS_FULL_D(pbs_curr_val, 2);
DEBUG_PBS_FULL_S(" PUP: ");
DEBUG_PBS_FULL_D(pup, 1);
DEBUG_PBS_FULL_S(" DQ: ");
DEBUG_PBS_FULL_D(dq, 1);
DEBUG_PBS_FULL_S(" - failed\n");
}
}
}
for (dq = 0; dq < DQ_NUM; dq++) {
unlock_pup_dq_array[dq] &=
cmp_unlock_pup_dq_array[pbs_cmp_retry][dq];
}
}
pup_locked = 0;
sum_pup_fail = *pcur_pup;
/* Check which DQ is failed */
for (dq = 0; dq < DQ_NUM; dq++) {
/* Summarize the locked pup */
pup_locked |= unlock_pup_dq_array[dq];
/* Check if get fail */
sum_pup_fail &= unlock_pup_dq_array[dq];
}
/* If all PUPS are locked in all DQ - Break */
if (pup_locked == 0) {
/* All pups are locked */
*start_over = 0;
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - All bit in all pups are successfully locked\n");
break;
}
/* PBS deskew elements reach max ? */
if (pbs_curr_val == last_pbs) {
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - PBS deskew elements reach max\n");
/* CHIP-ONLY! - Implementation Limitation */
*start_over = (sum_pup_fail != 0) && (!(*start_over));
*pcur_pup = pup_locked;
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - StartOver: ");
DEBUG_PBS_FULL_D(*start_over, 1);
DEBUG_PBS_FULL_S(" pup_locked: ");
DEBUG_PBS_FULL_D(pup_locked, 2);
DEBUG_PBS_FULL_S(" sum_pup_fail: ");
DEBUG_PBS_FULL_D(sum_pup_fail, 2);
DEBUG_PBS_FULL_S("\n");
/* Lock PBS value for all remaining bits */
for (pup = 0; pup < max_pup; pup++) {
/* Check if current pup already received error */
if (IS_PUP_ACTIVE(pup_locked, pup) == 1) {
/* Valid pup for current function */
if (IS_PUP_ACTIVE(sum_pup_fail, pup) ==
1 && (*start_over == 1)) {
DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - skipping lock of pup (first loop of pbs)",
pup, 1);
continue;
} else
if (IS_PUP_ACTIVE(sum_pup_fail, pup)
== 1) {
DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - Locking pup %d (even though it wasn't supposed to be locked)",
pup, 1);
}
/* Already got fail on the PUP */
/* Lock PBS value for all remaining bits */
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Locking remaning DQs for pup - ");
DEBUG_PBS_FULL_D(pup, 1);
DEBUG_PBS_FULL_S(": ");
for (dq = 0; dq < DQ_NUM; dq++) {
if (IS_PUP_ACTIVE
(unlock_pup_dq_array[dq],
pup) == 1) {
DEBUG_PBS_FULL_D(dq, 1);
DEBUG_PBS_FULL_S(",");
/* set current PBS */
skew_array[((pup) *
DQ_NUM) +
dq] =
pbs_curr_val;
}
}
if (*start_over == 1) {
/*
* Reset this pup bit - when
* restart the PBS, ignore this
* pup
*/
*pcur_pup &= ~(1 << pup);
}
DEBUG_PBS_FULL_S("\n");
} else {
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Pup ");
DEBUG_PBS_FULL_D(pup, 1);
DEBUG_PBS_FULL_C(" is not set in puplocked - ",
pup_locked, 1);
}
}
/* Need to start the PBS again */
if (*start_over == 1) {
DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - false fail - returning to start\n");
return MV_OK;
}
break;
}
/* Diff Check */
for (pup = 0; pup < max_pup; pup++) {
if (IS_PUP_ACTIVE(pup_locked, pup) == 1) {
/* pup is not locked */
if (first_failed[pup] == 0) {
/* No first fail until now */
if (IS_PUP_ACTIVE(sum_pup_fail, pup) ==
0) {
/* Get first fail */
DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - First fail in pup ",
pup, 1);
first_failed[pup] = 1;
first_fail[pup] = pbs_curr_val;
}
} else {
/* Already got first fail */
if (is_tx == 1) {
/* TX - inc pbs */
calc_pbs_diff = pbs_curr_val -
first_fail[pup];
} else {
/* RX - dec pbs */
calc_pbs_diff = first_fail[pup] -
pbs_curr_val;
}
if (calc_pbs_diff >= PBS_DIFF_LIMIT) {
lock_pups(pup, &pup_locked,
unlock_pup_dq_array,
pbs_curr_val,
start_pbs, ecc, is_tx);
}
}
}
}
} while (1);
return MV_OK;
}
/*
* Name: ddr3_set_pbs_results
* Desc: Set to HW the PBS phase results.
* Args: is_tx Indicates whether to set Tx or RX results
* Notes:
* Returns: MV_OK if success, other error code if fail.
*/
static int ddr3_set_pbs_results(MV_DRAM_INFO *dram_info, int is_tx)
{
u32 pup, phys_pup, dq;
u32 max_pup; /* number of valid pups */
u32 pbs_min; /* minimal pbs val per pup */
u32 pbs_max; /* maximum pbs val per pup */
u32 val[9];
max_pup = dram_info->num_of_total_pups;
DEBUG_PBS_FULL_S("DDR3 - PBS - ddr3_set_pbs_results:\n");
/* Loop for all dqs & pups */
for (pup = 0; pup < max_pup; pup++) {
if (pup == (max_pup - 1) && dram_info->ecc_ena)
phys_pup = ECC_PUP;
else
phys_pup = pup;
/*
* To minimize delay elements, inc from pbs value the min
* pbs val
*/
pbs_min = MAX_PBS;
pbs_max = 0;
for (dq = 0; dq < DQ_NUM; dq++) {
if (pbs_min > skew_array[(pup * DQ_NUM) + dq])
pbs_min = skew_array[(pup * DQ_NUM) + dq];
if (pbs_max < skew_array[(pup * DQ_NUM) + dq])
pbs_max = skew_array[(pup * DQ_NUM) + dq];
}
pbs_max -= pbs_min;
DEBUG_PBS_FULL_S("DDR3 - PBS - PUP");
DEBUG_PBS_FULL_D(phys_pup, 1);
DEBUG_PBS_FULL_S(": Min Val = ");
DEBUG_PBS_FULL_D(pbs_min, 2);
DEBUG_PBS_FULL_C(", Max Val = ", pbs_max, 2);
val[pup] = 0;
for (dq = 0; dq < DQ_NUM; dq++) {
int idx;
int offs;
/* Set skew value for all dq */
/*
* Bit# Deskew <- Bit# Deskew - last / first
* failing bit Deskew For all bits (per PUP)
* (minimize delay elements)
*/
DEBUG_PBS_FULL_S("DQ");
DEBUG_PBS_FULL_D(dq, 1);
DEBUG_PBS_FULL_S("-");
DEBUG_PBS_FULL_D((skew_array[(pup * DQ_NUM) + dq] -
pbs_min), 2);
DEBUG_PBS_FULL_S(", ");
idx = (pup * DQ_NUM) + dq;
if (is_tx == 1)
offs = PUP_PBS_TX;
else
offs = PUP_PBS_RX;
ddr3_write_pup_reg(offs + pbs_dq_mapping[phys_pup][dq],
CS0, phys_pup, 0,
skew_array[idx] - pbs_min);
if (is_tx == 1)
val[pup] += skew_array[idx] - pbs_min;
}
DEBUG_PBS_FULL_S("\n");
/* Set the DQS the half of the Max PBS of the DQs */
if (is_tx == 1) {
ddr3_write_pup_reg(PUP_PBS_TX + 8, CS0, phys_pup, 0,
pbs_max / 2);
ddr3_write_pup_reg(PUP_PBS_TX + 0xa, CS0, phys_pup, 0,
val[pup] / 8);
} else
ddr3_write_pup_reg(PUP_PBS_RX + 8, CS0, phys_pup, 0,
pbs_max / 2);
}
return MV_OK;
}
static void ddr3_pbs_write_pup_dqs_reg(u32 cs, u32 pup, u32 dqs_delay)
{
u32 reg, delay;
reg = (ddr3_read_pup_reg(PUP_WL_MODE, cs, pup) & 0x3FF);
delay = reg & PUP_DELAY_MASK;
reg |= ((dqs_delay + delay) << REG_PHY_DQS_REF_DLY_OFFS);
reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR;
reg |= (pup << REG_PHY_PUP_OFFS);
reg |= ((0x4 * cs + PUP_WL_MODE) << REG_PHY_CS_OFFS);
reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */
do {
reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
} while (reg); /* Wait for '0' to mark the end of the transaction */
udelay(10);
}
/*
* Set training patterns
*/
int ddr3_load_pbs_patterns(MV_DRAM_INFO *dram_info)
{
u32 cs, cs_count, cs_tmp;
u32 sdram_addr;
u32 *pattern_ptr0, *pattern_ptr1;
/* Choose pattern */
switch (dram_info->ddr_width) {
#if defined(MV88F672X)
case 16:
pattern_ptr0 = (u32 *)&pbs_pattern[0];
pattern_ptr1 = (u32 *)&pbs_pattern[1];
break;
#endif
case 32:
pattern_ptr0 = (u32 *)&pbs_pattern_32b[0];
pattern_ptr1 = (u32 *)&pbs_pattern_32b[1];
break;
#if defined(MV88F78X60)
case 64:
pattern_ptr0 = (u32 *)&pbs_pattern_64b[0];
pattern_ptr1 = (u32 *)&pbs_pattern_64b[1];
break;
#endif
default:
return MV_FAIL;
}
/* Loop for each CS */
for (cs = 0; cs < MAX_CS; cs++) {
if (dram_info->cs_ena & (1 << cs)) {
cs_count = 0;
for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) {
if (dram_info->cs_ena & (1 << cs_tmp))
cs_count++;
}
/* Init PBS I pattern */
sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) +
SDRAM_PBS_I_OFFS);
if (MV_OK !=
ddr3_sdram_compare(dram_info, (u32) NULL, NULL,
pattern_ptr0, LEN_STD_PATTERN,
sdram_addr, 1, 0, NULL,
0))
return MV_FAIL;
/* Init PBS II pattern */
sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) +
SDRAM_PBS_II_OFFS);
if (MV_OK !=
ddr3_sdram_compare(dram_info, (u32) NULL, NULL,
pattern_ptr1, LEN_STD_PATTERN,
sdram_addr, 1, 0, NULL,
0))
return MV_FAIL;
}
}
return MV_OK;
}
#endif