diff options
Diffstat (limited to 'drivers/ddr/mvebu/ddr3_pbs.c')
-rw-r--r-- | drivers/ddr/mvebu/ddr3_pbs.c | 1592 |
1 files changed, 1592 insertions, 0 deletions
diff --git a/drivers/ddr/mvebu/ddr3_pbs.c b/drivers/ddr/mvebu/ddr3_pbs.c new file mode 100644 index 0000000000..00ea3fdb91 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_pbs.c @@ -0,0 +1,1592 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" + +/* + * Debug + */ +#define DEBUG_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 |