|
|
|
@ -80,10 +80,6 @@ struct gbl_type *gbl; |
|
|
|
|
struct param_type *param; |
|
|
|
|
uint32_t curr_shadow_reg; |
|
|
|
|
|
|
|
|
|
static uint32_t rw_mgr_mem_calibrate_write_test(uint32_t rank_bgn, |
|
|
|
|
uint32_t write_group, uint32_t use_dm, |
|
|
|
|
uint32_t all_correct, uint32_t *bit_chk, uint32_t all_ranks); |
|
|
|
|
|
|
|
|
|
static void set_failing_group_stage(uint32_t group, uint32_t stage, |
|
|
|
|
uint32_t substage) |
|
|
|
|
{ |
|
|
|
@ -1036,6 +1032,207 @@ static void rw_mgr_mem_handoff(void) |
|
|
|
|
*/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* issue write test command. |
|
|
|
|
* two variants are provided. one that just tests a write pattern and |
|
|
|
|
* another that tests datamask functionality. |
|
|
|
|
*/ |
|
|
|
|
static void rw_mgr_mem_calibrate_write_test_issue(uint32_t group, |
|
|
|
|
uint32_t test_dm) |
|
|
|
|
{ |
|
|
|
|
uint32_t mcc_instruction; |
|
|
|
|
uint32_t quick_write_mode = (((STATIC_CALIB_STEPS) & CALIB_SKIP_WRITES) && |
|
|
|
|
ENABLE_SUPER_QUICK_CALIBRATION); |
|
|
|
|
uint32_t rw_wl_nop_cycles; |
|
|
|
|
uint32_t addr; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set counter and jump addresses for the right |
|
|
|
|
* number of NOP cycles. |
|
|
|
|
* The number of supported NOP cycles can range from -1 to infinity |
|
|
|
|
* Three different cases are handled: |
|
|
|
|
* |
|
|
|
|
* 1. For a number of NOP cycles greater than 0, the RW Mgr looping |
|
|
|
|
* mechanism will be used to insert the right number of NOPs |
|
|
|
|
* |
|
|
|
|
* 2. For a number of NOP cycles equals to 0, the micro-instruction |
|
|
|
|
* issuing the write command will jump straight to the |
|
|
|
|
* micro-instruction that turns on DQS (for DDRx), or outputs write |
|
|
|
|
* data (for RLD), skipping |
|
|
|
|
* the NOP micro-instruction all together |
|
|
|
|
* |
|
|
|
|
* 3. A number of NOP cycles equal to -1 indicates that DQS must be |
|
|
|
|
* turned on in the same micro-instruction that issues the write |
|
|
|
|
* command. Then we need |
|
|
|
|
* to directly jump to the micro-instruction that sends out the data |
|
|
|
|
* |
|
|
|
|
* NOTE: Implementing this mechanism uses 2 RW Mgr jump-counters |
|
|
|
|
* (2 and 3). One jump-counter (0) is used to perform multiple |
|
|
|
|
* write-read operations. |
|
|
|
|
* one counter left to issue this command in "multiple-group" mode |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
rw_wl_nop_cycles = gbl->rw_wl_nop_cycles; |
|
|
|
|
|
|
|
|
|
if (rw_wl_nop_cycles == -1) { |
|
|
|
|
/*
|
|
|
|
|
* CNTR 2 - We want to execute the special write operation that |
|
|
|
|
* turns on DQS right away and then skip directly to the |
|
|
|
|
* instruction that sends out the data. We set the counter to a |
|
|
|
|
* large number so that the jump is always taken. |
|
|
|
|
*/ |
|
|
|
|
writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2); |
|
|
|
|
|
|
|
|
|
/* CNTR 3 - Not used */ |
|
|
|
|
if (test_dm) { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0_WL_1; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DATA, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} else { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0_WL_1; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_DATA, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} |
|
|
|
|
} else if (rw_wl_nop_cycles == 0) { |
|
|
|
|
/*
|
|
|
|
|
* CNTR 2 - We want to skip the NOP operation and go straight |
|
|
|
|
* to the DQS enable instruction. We set the counter to a large |
|
|
|
|
* number so that the jump is always taken. |
|
|
|
|
*/ |
|
|
|
|
writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2); |
|
|
|
|
|
|
|
|
|
/* CNTR 3 - Not used */ |
|
|
|
|
if (test_dm) { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DQS, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
} else { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_DQS, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
/*
|
|
|
|
|
* CNTR 2 - In this case we want to execute the next instruction |
|
|
|
|
* and NOT take the jump. So we set the counter to 0. The jump |
|
|
|
|
* address doesn't count. |
|
|
|
|
*/ |
|
|
|
|
writel(0x0, &sdr_rw_load_mgr_regs->load_cntr2); |
|
|
|
|
writel(0x0, &sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* CNTR 3 - Set the nop counter to the number of cycles we |
|
|
|
|
* need to loop for, minus 1. |
|
|
|
|
*/ |
|
|
|
|
writel(rw_wl_nop_cycles - 1, &sdr_rw_load_mgr_regs->load_cntr3); |
|
|
|
|
if (test_dm) { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} else { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS | |
|
|
|
|
RW_MGR_RESET_READ_DATAPATH_OFFSET); |
|
|
|
|
|
|
|
|
|
if (quick_write_mode) |
|
|
|
|
writel(0x08, &sdr_rw_load_mgr_regs->load_cntr0); |
|
|
|
|
else |
|
|
|
|
writel(0x40, &sdr_rw_load_mgr_regs->load_cntr0); |
|
|
|
|
|
|
|
|
|
writel(mcc_instruction, &sdr_rw_load_jump_mgr_regs->load_jump_add0); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* CNTR 1 - This is used to ensure enough time elapses |
|
|
|
|
* for read data to come back. |
|
|
|
|
*/ |
|
|
|
|
writel(0x30, &sdr_rw_load_mgr_regs->load_cntr1); |
|
|
|
|
|
|
|
|
|
if (test_dm) { |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_WAIT, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add1); |
|
|
|
|
} else { |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_WAIT, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_RUN_SINGLE_GROUP_OFFSET; |
|
|
|
|
writel(mcc_instruction, addr + (group << 2)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Test writes, can check for a single bit pass or multiple bit pass */ |
|
|
|
|
static uint32_t rw_mgr_mem_calibrate_write_test(uint32_t rank_bgn, |
|
|
|
|
uint32_t write_group, uint32_t use_dm, uint32_t all_correct, |
|
|
|
|
uint32_t *bit_chk, uint32_t all_ranks) |
|
|
|
|
{ |
|
|
|
|
uint32_t r; |
|
|
|
|
uint32_t correct_mask_vg; |
|
|
|
|
uint32_t tmp_bit_chk; |
|
|
|
|
uint32_t vg; |
|
|
|
|
uint32_t rank_end = all_ranks ? RW_MGR_MEM_NUMBER_OF_RANKS : |
|
|
|
|
(rank_bgn + NUM_RANKS_PER_SHADOW_REG); |
|
|
|
|
uint32_t addr_rw_mgr; |
|
|
|
|
uint32_t base_rw_mgr; |
|
|
|
|
|
|
|
|
|
*bit_chk = param->write_correct_mask; |
|
|
|
|
correct_mask_vg = param->write_correct_mask_vg; |
|
|
|
|
|
|
|
|
|
for (r = rank_bgn; r < rank_end; r++) { |
|
|
|
|
if (param->skip_ranks[r]) { |
|
|
|
|
/* request to skip the rank */ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* set rank */ |
|
|
|
|
set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_READ_WRITE); |
|
|
|
|
|
|
|
|
|
tmp_bit_chk = 0; |
|
|
|
|
addr_rw_mgr = SDR_PHYGRP_RWMGRGRP_ADDRESS; |
|
|
|
|
for (vg = RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS-1; ; vg--) { |
|
|
|
|
/* reset the fifos to get pointers to known state */ |
|
|
|
|
writel(0, &phy_mgr_cmd->fifo_reset); |
|
|
|
|
|
|
|
|
|
tmp_bit_chk = tmp_bit_chk << |
|
|
|
|
(RW_MGR_MEM_DQ_PER_WRITE_DQS / |
|
|
|
|
RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS); |
|
|
|
|
rw_mgr_mem_calibrate_write_test_issue(write_group * |
|
|
|
|
RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS+vg, |
|
|
|
|
use_dm); |
|
|
|
|
|
|
|
|
|
base_rw_mgr = readl(addr_rw_mgr); |
|
|
|
|
tmp_bit_chk = tmp_bit_chk | (correct_mask_vg & ~(base_rw_mgr)); |
|
|
|
|
if (vg == 0) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
*bit_chk &= tmp_bit_chk; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (all_correct) { |
|
|
|
|
set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); |
|
|
|
|
debug_cond(DLEVEL == 2, "write_test(%u,%u,ALL) : %u == \
|
|
|
|
|
%u => %lu", write_group, use_dm, |
|
|
|
|
*bit_chk, param->write_correct_mask, |
|
|
|
|
(long unsigned int)(*bit_chk == |
|
|
|
|
param->write_correct_mask)); |
|
|
|
|
return *bit_chk == param->write_correct_mask; |
|
|
|
|
} else { |
|
|
|
|
set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); |
|
|
|
|
debug_cond(DLEVEL == 2, "write_test(%u,%u,ONE) : %u != ", |
|
|
|
|
write_group, use_dm, *bit_chk); |
|
|
|
|
debug_cond(DLEVEL == 2, "%lu" " => %lu", (long unsigned int)0, |
|
|
|
|
(long unsigned int)(*bit_chk != 0)); |
|
|
|
|
return *bit_chk != 0x00; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* rw_mgr_mem_calibrate_read_test_patterns() - Read back test patterns |
|
|
|
|
* @rank_bgn: Rank number |
|
|
|
@ -2682,207 +2879,6 @@ static uint32_t rw_mgr_mem_calibrate_lfifo(void) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* issue write test command. |
|
|
|
|
* two variants are provided. one that just tests a write pattern and |
|
|
|
|
* another that tests datamask functionality. |
|
|
|
|
*/ |
|
|
|
|
static void rw_mgr_mem_calibrate_write_test_issue(uint32_t group, |
|
|
|
|
uint32_t test_dm) |
|
|
|
|
{ |
|
|
|
|
uint32_t mcc_instruction; |
|
|
|
|
uint32_t quick_write_mode = (((STATIC_CALIB_STEPS) & CALIB_SKIP_WRITES) && |
|
|
|
|
ENABLE_SUPER_QUICK_CALIBRATION); |
|
|
|
|
uint32_t rw_wl_nop_cycles; |
|
|
|
|
uint32_t addr; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set counter and jump addresses for the right |
|
|
|
|
* number of NOP cycles. |
|
|
|
|
* The number of supported NOP cycles can range from -1 to infinity |
|
|
|
|
* Three different cases are handled: |
|
|
|
|
* |
|
|
|
|
* 1. For a number of NOP cycles greater than 0, the RW Mgr looping |
|
|
|
|
* mechanism will be used to insert the right number of NOPs |
|
|
|
|
* |
|
|
|
|
* 2. For a number of NOP cycles equals to 0, the micro-instruction |
|
|
|
|
* issuing the write command will jump straight to the |
|
|
|
|
* micro-instruction that turns on DQS (for DDRx), or outputs write |
|
|
|
|
* data (for RLD), skipping |
|
|
|
|
* the NOP micro-instruction all together |
|
|
|
|
* |
|
|
|
|
* 3. A number of NOP cycles equal to -1 indicates that DQS must be |
|
|
|
|
* turned on in the same micro-instruction that issues the write |
|
|
|
|
* command. Then we need |
|
|
|
|
* to directly jump to the micro-instruction that sends out the data |
|
|
|
|
* |
|
|
|
|
* NOTE: Implementing this mechanism uses 2 RW Mgr jump-counters |
|
|
|
|
* (2 and 3). One jump-counter (0) is used to perform multiple |
|
|
|
|
* write-read operations. |
|
|
|
|
* one counter left to issue this command in "multiple-group" mode |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
rw_wl_nop_cycles = gbl->rw_wl_nop_cycles; |
|
|
|
|
|
|
|
|
|
if (rw_wl_nop_cycles == -1) { |
|
|
|
|
/*
|
|
|
|
|
* CNTR 2 - We want to execute the special write operation that |
|
|
|
|
* turns on DQS right away and then skip directly to the |
|
|
|
|
* instruction that sends out the data. We set the counter to a |
|
|
|
|
* large number so that the jump is always taken. |
|
|
|
|
*/ |
|
|
|
|
writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2); |
|
|
|
|
|
|
|
|
|
/* CNTR 3 - Not used */ |
|
|
|
|
if (test_dm) { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0_WL_1; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DATA, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} else { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0_WL_1; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_DATA, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} |
|
|
|
|
} else if (rw_wl_nop_cycles == 0) { |
|
|
|
|
/*
|
|
|
|
|
* CNTR 2 - We want to skip the NOP operation and go straight |
|
|
|
|
* to the DQS enable instruction. We set the counter to a large |
|
|
|
|
* number so that the jump is always taken. |
|
|
|
|
*/ |
|
|
|
|
writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2); |
|
|
|
|
|
|
|
|
|
/* CNTR 3 - Not used */ |
|
|
|
|
if (test_dm) { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DQS, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
} else { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_DQS, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
/*
|
|
|
|
|
* CNTR 2 - In this case we want to execute the next instruction |
|
|
|
|
* and NOT take the jump. So we set the counter to 0. The jump |
|
|
|
|
* address doesn't count. |
|
|
|
|
*/ |
|
|
|
|
writel(0x0, &sdr_rw_load_mgr_regs->load_cntr2); |
|
|
|
|
writel(0x0, &sdr_rw_load_jump_mgr_regs->load_jump_add2); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* CNTR 3 - Set the nop counter to the number of cycles we |
|
|
|
|
* need to loop for, minus 1. |
|
|
|
|
*/ |
|
|
|
|
writel(rw_wl_nop_cycles - 1, &sdr_rw_load_mgr_regs->load_cntr3); |
|
|
|
|
if (test_dm) { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} else { |
|
|
|
|
mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0; |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add3); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS | |
|
|
|
|
RW_MGR_RESET_READ_DATAPATH_OFFSET); |
|
|
|
|
|
|
|
|
|
if (quick_write_mode) |
|
|
|
|
writel(0x08, &sdr_rw_load_mgr_regs->load_cntr0); |
|
|
|
|
else |
|
|
|
|
writel(0x40, &sdr_rw_load_mgr_regs->load_cntr0); |
|
|
|
|
|
|
|
|
|
writel(mcc_instruction, &sdr_rw_load_jump_mgr_regs->load_jump_add0); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* CNTR 1 - This is used to ensure enough time elapses |
|
|
|
|
* for read data to come back. |
|
|
|
|
*/ |
|
|
|
|
writel(0x30, &sdr_rw_load_mgr_regs->load_cntr1); |
|
|
|
|
|
|
|
|
|
if (test_dm) { |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_WAIT, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add1); |
|
|
|
|
} else { |
|
|
|
|
writel(RW_MGR_LFSR_WR_RD_BANK_0_WAIT, |
|
|
|
|
&sdr_rw_load_jump_mgr_regs->load_jump_add1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_RUN_SINGLE_GROUP_OFFSET; |
|
|
|
|
writel(mcc_instruction, addr + (group << 2)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Test writes, can check for a single bit pass or multiple bit pass */ |
|
|
|
|
static uint32_t rw_mgr_mem_calibrate_write_test(uint32_t rank_bgn, |
|
|
|
|
uint32_t write_group, uint32_t use_dm, uint32_t all_correct, |
|
|
|
|
uint32_t *bit_chk, uint32_t all_ranks) |
|
|
|
|
{ |
|
|
|
|
uint32_t r; |
|
|
|
|
uint32_t correct_mask_vg; |
|
|
|
|
uint32_t tmp_bit_chk; |
|
|
|
|
uint32_t vg; |
|
|
|
|
uint32_t rank_end = all_ranks ? RW_MGR_MEM_NUMBER_OF_RANKS : |
|
|
|
|
(rank_bgn + NUM_RANKS_PER_SHADOW_REG); |
|
|
|
|
uint32_t addr_rw_mgr; |
|
|
|
|
uint32_t base_rw_mgr; |
|
|
|
|
|
|
|
|
|
*bit_chk = param->write_correct_mask; |
|
|
|
|
correct_mask_vg = param->write_correct_mask_vg; |
|
|
|
|
|
|
|
|
|
for (r = rank_bgn; r < rank_end; r++) { |
|
|
|
|
if (param->skip_ranks[r]) { |
|
|
|
|
/* request to skip the rank */ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* set rank */ |
|
|
|
|
set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_READ_WRITE); |
|
|
|
|
|
|
|
|
|
tmp_bit_chk = 0; |
|
|
|
|
addr_rw_mgr = SDR_PHYGRP_RWMGRGRP_ADDRESS; |
|
|
|
|
for (vg = RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS-1; ; vg--) { |
|
|
|
|
/* reset the fifos to get pointers to known state */ |
|
|
|
|
writel(0, &phy_mgr_cmd->fifo_reset); |
|
|
|
|
|
|
|
|
|
tmp_bit_chk = tmp_bit_chk << |
|
|
|
|
(RW_MGR_MEM_DQ_PER_WRITE_DQS / |
|
|
|
|
RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS); |
|
|
|
|
rw_mgr_mem_calibrate_write_test_issue(write_group * |
|
|
|
|
RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS+vg, |
|
|
|
|
use_dm); |
|
|
|
|
|
|
|
|
|
base_rw_mgr = readl(addr_rw_mgr); |
|
|
|
|
tmp_bit_chk = tmp_bit_chk | (correct_mask_vg & ~(base_rw_mgr)); |
|
|
|
|
if (vg == 0) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
*bit_chk &= tmp_bit_chk; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (all_correct) { |
|
|
|
|
set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); |
|
|
|
|
debug_cond(DLEVEL == 2, "write_test(%u,%u,ALL) : %u == \
|
|
|
|
|
%u => %lu", write_group, use_dm, |
|
|
|
|
*bit_chk, param->write_correct_mask, |
|
|
|
|
(long unsigned int)(*bit_chk == |
|
|
|
|
param->write_correct_mask)); |
|
|
|
|
return *bit_chk == param->write_correct_mask; |
|
|
|
|
} else { |
|
|
|
|
set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); |
|
|
|
|
debug_cond(DLEVEL == 2, "write_test(%u,%u,ONE) : %u != ", |
|
|
|
|
write_group, use_dm, *bit_chk); |
|
|
|
|
debug_cond(DLEVEL == 2, "%lu" " => %lu", (long unsigned int)0, |
|
|
|
|
(long unsigned int)(*bit_chk != 0)); |
|
|
|
|
return *bit_chk != 0x00; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* search_window() - Search for the/part of the window with DM/DQS shift |
|
|
|
|
* @search_dm: If 1, search for the DM shift, if 0, search for DQS shift |
|
|
|
|