// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) Marvell International Ltd. and its affiliates */ #include #include #include #include #include #include #include #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