This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the Armada A38x boot image. Not linked with the main U-Boot. With this code addition and the serdes/PHY setup code, the Armada A38x support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Note: This code has undergone many hours (days!) of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. Signed-off-by: Stefan Roese <sr@denx.de>
1355 lines
42 KiB
C
1355 lines
42 KiB
C
/*
|
|
* Copyright (C) Marvell International Ltd. and its affiliates
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <spl.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/cpu.h>
|
|
#include <asm/arch/soc.h>
|
|
|
|
#include "ddr3_init.h"
|
|
|
|
#define PATTERN_1 0x55555555
|
|
#define PATTERN_2 0xaaaaaaaa
|
|
|
|
#define VALIDATE_TRAINING_LIMIT(e1, e2) \
|
|
((((e2) - (e1) + 1) > 33) && ((e1) < 67))
|
|
|
|
u32 phy_reg_bk[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS];
|
|
|
|
u32 training_res[MAX_INTERFACE_NUM * MAX_BUS_NUM * BUS_WIDTH_IN_BITS *
|
|
HWS_SEARCH_DIR_LIMIT];
|
|
|
|
u16 mask_results_dq_reg_map[] = {
|
|
RESULT_CONTROL_PUP_0_BIT_0_REG, RESULT_CONTROL_PUP_0_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_0_BIT_2_REG, RESULT_CONTROL_PUP_0_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_0_BIT_4_REG, RESULT_CONTROL_PUP_0_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_0_BIT_6_REG, RESULT_CONTROL_PUP_0_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_0_REG, RESULT_CONTROL_PUP_1_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_2_REG, RESULT_CONTROL_PUP_1_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_4_REG, RESULT_CONTROL_PUP_1_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_6_REG, RESULT_CONTROL_PUP_1_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_0_REG, RESULT_CONTROL_PUP_2_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_2_REG, RESULT_CONTROL_PUP_2_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_4_REG, RESULT_CONTROL_PUP_2_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_6_REG, RESULT_CONTROL_PUP_2_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_3_BIT_0_REG, RESULT_CONTROL_PUP_3_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_3_BIT_2_REG, RESULT_CONTROL_PUP_3_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_3_BIT_4_REG, RESULT_CONTROL_PUP_3_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_3_BIT_6_REG, RESULT_CONTROL_PUP_3_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG,
|
|
};
|
|
|
|
u16 mask_results_pup_reg_map[] = {
|
|
RESULT_CONTROL_BYTE_PUP_0_REG, RESULT_CONTROL_BYTE_PUP_1_REG,
|
|
RESULT_CONTROL_BYTE_PUP_2_REG, RESULT_CONTROL_BYTE_PUP_3_REG,
|
|
RESULT_CONTROL_BYTE_PUP_4_REG
|
|
};
|
|
|
|
u16 mask_results_dq_reg_map_pup3_ecc[] = {
|
|
RESULT_CONTROL_PUP_0_BIT_0_REG, RESULT_CONTROL_PUP_0_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_0_BIT_2_REG, RESULT_CONTROL_PUP_0_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_0_BIT_4_REG, RESULT_CONTROL_PUP_0_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_0_BIT_6_REG, RESULT_CONTROL_PUP_0_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_0_REG, RESULT_CONTROL_PUP_1_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_2_REG, RESULT_CONTROL_PUP_1_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_4_REG, RESULT_CONTROL_PUP_1_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_1_BIT_6_REG, RESULT_CONTROL_PUP_1_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_0_REG, RESULT_CONTROL_PUP_2_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_2_REG, RESULT_CONTROL_PUP_2_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_4_REG, RESULT_CONTROL_PUP_2_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_2_BIT_6_REG, RESULT_CONTROL_PUP_2_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG,
|
|
RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG,
|
|
};
|
|
|
|
u16 mask_results_pup_reg_map_pup3_ecc[] = {
|
|
RESULT_CONTROL_BYTE_PUP_0_REG, RESULT_CONTROL_BYTE_PUP_1_REG,
|
|
RESULT_CONTROL_BYTE_PUP_2_REG, RESULT_CONTROL_BYTE_PUP_4_REG,
|
|
RESULT_CONTROL_BYTE_PUP_4_REG
|
|
};
|
|
|
|
struct pattern_info pattern_table_16[] = {
|
|
/*
|
|
* num tx phases, tx burst, delay between, rx pattern,
|
|
* start_address, pattern_len
|
|
*/
|
|
{1, 1, 2, 1, 0x0080, 2}, /* PATTERN_PBS1 */
|
|
{1, 1, 2, 1, 0x00c0, 2}, /* PATTERN_PBS2 */
|
|
{1, 1, 2, 1, 0x0100, 2}, /* PATTERN_RL */
|
|
{0xf, 0x7, 2, 0x7, 0x0140, 16}, /* PATTERN_STATIC_PBS */
|
|
{0xf, 0x7, 2, 0x7, 0x0190, 16}, /* PATTERN_KILLER_DQ0 */
|
|
{0xf, 0x7, 2, 0x7, 0x01d0, 16}, /* PATTERN_KILLER_DQ1 */
|
|
{0xf, 0x7, 2, 0x7, 0x0210, 16}, /* PATTERN_KILLER_DQ2 */
|
|
{0xf, 0x7, 2, 0x7, 0x0250, 16}, /* PATTERN_KILLER_DQ3 */
|
|
{0xf, 0x7, 2, 0x7, 0x0290, 16}, /* PATTERN_KILLER_DQ4 */
|
|
{0xf, 0x7, 2, 0x7, 0x02d0, 16}, /* PATTERN_KILLER_DQ5 */
|
|
{0xf, 0x7, 2, 0x7, 0x0310, 16}, /* PATTERN_KILLER_DQ6 */
|
|
{0xf, 0x7, 2, 0x7, 0x0350, 16}, /* PATTERN_KILLER_DQ7 */
|
|
{1, 1, 2, 1, 0x0380, 2}, /* PATTERN_PBS3 */
|
|
{1, 1, 2, 1, 0x0000, 2}, /* PATTERN_RL2 */
|
|
{1, 1, 2, 1, 0x0040, 2}, /* PATTERN_TEST */
|
|
{0xf, 0x7, 2, 0x7, 0x03c0, 16}, /* PATTERN_FULL_SSO_1T */
|
|
{0xf, 0x7, 2, 0x7, 0x0400, 16}, /* PATTERN_FULL_SSO_2T */
|
|
{0xf, 0x7, 2, 0x7, 0x0440, 16}, /* PATTERN_FULL_SSO_3T */
|
|
{0xf, 0x7, 2, 0x7, 0x0480, 16}, /* PATTERN_FULL_SSO_4T */
|
|
{0xf, 0x7, 2, 0x7, 0x04c0, 16} /* PATTERN_VREF */
|
|
/*Note: actual start_address is <<3 of defined addess */
|
|
};
|
|
|
|
struct pattern_info pattern_table_32[] = {
|
|
/*
|
|
* num tx phases, tx burst, delay between, rx pattern,
|
|
* start_address, pattern_len
|
|
*/
|
|
{3, 3, 2, 3, 0x0080, 4}, /* PATTERN_PBS1 */
|
|
{3, 3, 2, 3, 0x00c0, 4}, /* PATTERN_PBS2 */
|
|
{3, 3, 2, 3, 0x0100, 4}, /* PATTERN_RL */
|
|
{0x1f, 0xf, 2, 0xf, 0x0140, 32}, /* PATTERN_STATIC_PBS */
|
|
{0x1f, 0xf, 2, 0xf, 0x0190, 32}, /* PATTERN_KILLER_DQ0 */
|
|
{0x1f, 0xf, 2, 0xf, 0x01d0, 32}, /* PATTERN_KILLER_DQ1 */
|
|
{0x1f, 0xf, 2, 0xf, 0x0210, 32}, /* PATTERN_KILLER_DQ2 */
|
|
{0x1f, 0xf, 2, 0xf, 0x0250, 32}, /* PATTERN_KILLER_DQ3 */
|
|
{0x1f, 0xf, 2, 0xf, 0x0290, 32}, /* PATTERN_KILLER_DQ4 */
|
|
{0x1f, 0xf, 2, 0xf, 0x02d0, 32}, /* PATTERN_KILLER_DQ5 */
|
|
{0x1f, 0xf, 2, 0xf, 0x0310, 32}, /* PATTERN_KILLER_DQ6 */
|
|
{0x1f, 0xf, 2, 0xf, 0x0350, 32}, /* PATTERN_KILLER_DQ7 */
|
|
{3, 3, 2, 3, 0x0380, 4}, /* PATTERN_PBS3 */
|
|
{3, 3, 2, 3, 0x0000, 4}, /* PATTERN_RL2 */
|
|
{3, 3, 2, 3, 0x0040, 4}, /* PATTERN_TEST */
|
|
{0x1f, 0xf, 2, 0xf, 0x03c0, 32}, /* PATTERN_FULL_SSO_1T */
|
|
{0x1f, 0xf, 2, 0xf, 0x0400, 32}, /* PATTERN_FULL_SSO_2T */
|
|
{0x1f, 0xf, 2, 0xf, 0x0440, 32}, /* PATTERN_FULL_SSO_3T */
|
|
{0x1f, 0xf, 2, 0xf, 0x0480, 32}, /* PATTERN_FULL_SSO_4T */
|
|
{0x1f, 0xf, 2, 0xf, 0x04c0, 32} /* PATTERN_VREF */
|
|
/*Note: actual start_address is <<3 of defined addess */
|
|
};
|
|
|
|
u32 train_dev_num;
|
|
enum hws_ddr_cs traintrain_cs_type;
|
|
u32 train_pup_num;
|
|
enum hws_training_result train_result_type;
|
|
enum hws_control_element train_control_element;
|
|
enum hws_search_dir traine_search_dir;
|
|
enum hws_dir train_direction;
|
|
u32 train_if_select;
|
|
u32 train_init_value;
|
|
u32 train_number_iterations;
|
|
enum hws_pattern train_pattern;
|
|
enum hws_edge_compare train_edge_compare;
|
|
u32 train_cs_num;
|
|
u32 train_if_acess, train_if_id, train_pup_access;
|
|
u32 max_polling_for_done = 1000000;
|
|
|
|
u32 *ddr3_tip_get_buf_ptr(u32 dev_num, enum hws_search_dir search,
|
|
enum hws_training_result result_type,
|
|
u32 interface_num)
|
|
{
|
|
u32 *buf_ptr = NULL;
|
|
|
|
buf_ptr = &training_res
|
|
[MAX_INTERFACE_NUM * MAX_BUS_NUM * BUS_WIDTH_IN_BITS * search +
|
|
interface_num * MAX_BUS_NUM * BUS_WIDTH_IN_BITS];
|
|
|
|
return buf_ptr;
|
|
}
|
|
|
|
/*
|
|
* IP Training search
|
|
* Note: for one edge search only from fail to pass, else jitter can
|
|
* be be entered into solution.
|
|
*/
|
|
int ddr3_tip_ip_training(u32 dev_num, enum hws_access_type access_type,
|
|
u32 interface_num,
|
|
enum hws_access_type pup_access_type,
|
|
u32 pup_num, enum hws_training_result result_type,
|
|
enum hws_control_element control_element,
|
|
enum hws_search_dir search_dir, enum hws_dir direction,
|
|
u32 interface_mask, u32 init_value, u32 num_iter,
|
|
enum hws_pattern pattern,
|
|
enum hws_edge_compare edge_comp,
|
|
enum hws_ddr_cs cs_type, u32 cs_num,
|
|
enum hws_training_ip_stat *train_status)
|
|
{
|
|
u32 mask_dq_num_of_regs, mask_pup_num_of_regs, index_cnt, poll_cnt,
|
|
reg_data, pup_id;
|
|
u32 tx_burst_size;
|
|
u32 delay_between_burst;
|
|
u32 rd_mode;
|
|
u32 read_data[MAX_INTERFACE_NUM];
|
|
struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
|
|
u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
|
|
u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
if (pup_num >= tm->num_of_bus_per_interface) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("pup_num %d not valid\n", pup_num));
|
|
}
|
|
if (interface_num >= MAX_INTERFACE_NUM) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("if_id %d not valid\n",
|
|
interface_num));
|
|
}
|
|
if (train_status == NULL) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("error param 4\n"));
|
|
return MV_BAD_PARAM;
|
|
}
|
|
|
|
/* load pattern */
|
|
if (cs_type == CS_SINGLE) {
|
|
/* All CSs to CS0 */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
CS_ENABLE_REG, 1 << 3, 1 << 3));
|
|
/* All CSs to CS0 */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
ODPG_DATA_CONTROL_REG,
|
|
(0x3 | (effective_cs << 26)), 0xc000003));
|
|
} else {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
CS_ENABLE_REG, 0, 1 << 3));
|
|
/* CS select */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
ODPG_DATA_CONTROL_REG, 0x3 | cs_num << 26,
|
|
0x3 | 3 << 26));
|
|
}
|
|
|
|
/* load pattern to ODPG */
|
|
ddr3_tip_load_pattern_to_odpg(dev_num, access_type, interface_num,
|
|
pattern,
|
|
pattern_table[pattern].start_addr);
|
|
tx_burst_size = (direction == OPER_WRITE) ?
|
|
pattern_table[pattern].tx_burst_size : 0;
|
|
delay_between_burst = (direction == OPER_WRITE) ? 2 : 0;
|
|
rd_mode = (direction == OPER_WRITE) ? 1 : 0;
|
|
CHECK_STATUS(ddr3_tip_configure_odpg
|
|
(dev_num, access_type, interface_num, direction,
|
|
pattern_table[pattern].num_of_phases_tx, tx_burst_size,
|
|
pattern_table[pattern].num_of_phases_rx,
|
|
delay_between_burst, rd_mode, effective_cs, STRESS_NONE,
|
|
DURATION_SINGLE));
|
|
reg_data = (direction == OPER_READ) ? 0 : (0x3 << 30);
|
|
reg_data |= (direction == OPER_READ) ? 0x60 : 0xfa;
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
ODPG_WRITE_READ_MODE_ENABLE_REG, reg_data,
|
|
MASK_ALL_BITS));
|
|
reg_data = (edge_comp == EDGE_PF || edge_comp == EDGE_FP) ? 0 : 1 << 6;
|
|
reg_data |= (edge_comp == EDGE_PF || edge_comp == EDGE_PFP) ?
|
|
(1 << 7) : 0;
|
|
|
|
/* change from Pass to Fail will lock the result */
|
|
if (pup_access_type == ACCESS_TYPE_MULTICAST)
|
|
reg_data |= 0xe << 14;
|
|
else
|
|
reg_data |= pup_num << 14;
|
|
|
|
if (edge_comp == EDGE_FP) {
|
|
/* don't search for readl edge change, only the state */
|
|
reg_data |= (0 << 20);
|
|
} else if (edge_comp == EDGE_FPF) {
|
|
reg_data |= (0 << 20);
|
|
} else {
|
|
reg_data |= (3 << 20);
|
|
}
|
|
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
ODPG_TRAINING_CONTROL_REG,
|
|
reg_data | (0x7 << 8) | (0x7 << 11),
|
|
(0x3 | (0x3 << 2) | (0x3 << 6) | (1 << 5) | (0x7 << 8) |
|
|
(0x7 << 11) | (0xf << 14) | (0x3 << 18) | (3 << 20))));
|
|
reg_data = (search_dir == HWS_LOW2HIGH) ? 0 : (1 << 8);
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num, ODPG_OBJ1_OPCODE_REG,
|
|
1 | reg_data | init_value << 9 | (1 << 25) | (1 << 26),
|
|
0xff | (1 << 8) | (0xffff << 9) | (1 << 25) | (1 << 26)));
|
|
|
|
/*
|
|
* Write2_dunit(0x10b4, Number_iteration , [15:0])
|
|
* Max number of iterations
|
|
*/
|
|
CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, interface_num,
|
|
ODPG_OBJ1_ITER_CNT_REG, num_iter,
|
|
0xffff));
|
|
if (control_element == HWS_CONTROL_ELEMENT_DQ_SKEW &&
|
|
direction == OPER_READ) {
|
|
/*
|
|
* Write2_dunit(0x10c0, 0x5f , [7:0])
|
|
* MC PBS Reg Address at DDR PHY
|
|
*/
|
|
reg_data = 0x5f +
|
|
effective_cs * CALIBRATED_OBJECTS_REG_ADDR_OFFSET;
|
|
} else if (control_element == HWS_CONTROL_ELEMENT_DQ_SKEW &&
|
|
direction == OPER_WRITE) {
|
|
reg_data = 0x1f +
|
|
effective_cs * CALIBRATED_OBJECTS_REG_ADDR_OFFSET;
|
|
} else if (control_element == HWS_CONTROL_ELEMENT_ADLL &&
|
|
direction == OPER_WRITE) {
|
|
/*
|
|
* LOOP 0x00000001 + 4*n:
|
|
* where n (0-3) represents M_CS number
|
|
*/
|
|
/*
|
|
* Write2_dunit(0x10c0, 0x1 , [7:0])
|
|
* ADLL WR Reg Address at DDR PHY
|
|
*/
|
|
reg_data = 1 + effective_cs * CS_REGISTER_ADDR_OFFSET;
|
|
} else if (control_element == HWS_CONTROL_ELEMENT_ADLL &&
|
|
direction == OPER_READ) {
|
|
/* ADLL RD Reg Address at DDR PHY */
|
|
reg_data = 3 + effective_cs * CS_REGISTER_ADDR_OFFSET;
|
|
} else if (control_element == HWS_CONTROL_ELEMENT_DQS_SKEW &&
|
|
direction == OPER_WRITE) {
|
|
/* TBD not defined in 0.5.0 requirement */
|
|
} else if (control_element == HWS_CONTROL_ELEMENT_DQS_SKEW &&
|
|
direction == OPER_READ) {
|
|
/* TBD not defined in 0.5.0 requirement */
|
|
}
|
|
|
|
reg_data |= (0x6 << 28);
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num, CALIB_OBJ_PRFA_REG,
|
|
reg_data | (init_value << 8),
|
|
0xff | (0xffff << 8) | (0xf << 24) | (u32) (0xf << 28)));
|
|
|
|
mask_dq_num_of_regs = tm->num_of_bus_per_interface * BUS_WIDTH_IN_BITS;
|
|
mask_pup_num_of_regs = tm->num_of_bus_per_interface;
|
|
|
|
if (result_type == RESULT_PER_BIT) {
|
|
for (index_cnt = 0; index_cnt < mask_dq_num_of_regs;
|
|
index_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
mask_results_dq_reg_map[index_cnt], 0,
|
|
1 << 24));
|
|
}
|
|
|
|
/* Mask disabled buses */
|
|
for (pup_id = 0; pup_id < tm->num_of_bus_per_interface;
|
|
pup_id++) {
|
|
if (IS_ACTIVE(tm->bus_act_mask, pup_id) == 1)
|
|
continue;
|
|
|
|
for (index_cnt = (mask_dq_num_of_regs - pup_id * 8);
|
|
index_cnt <
|
|
(mask_dq_num_of_regs - (pup_id + 1) * 8);
|
|
index_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type,
|
|
interface_num,
|
|
mask_results_dq_reg_map
|
|
[index_cnt], (1 << 24), 1 << 24));
|
|
}
|
|
}
|
|
|
|
for (index_cnt = 0; index_cnt < mask_pup_num_of_regs;
|
|
index_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
mask_results_pup_reg_map[index_cnt],
|
|
(1 << 24), 1 << 24));
|
|
}
|
|
} else if (result_type == RESULT_PER_BYTE) {
|
|
/* write to adll */
|
|
for (index_cnt = 0; index_cnt < mask_pup_num_of_regs;
|
|
index_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
mask_results_pup_reg_map[index_cnt], 0,
|
|
1 << 24));
|
|
}
|
|
for (index_cnt = 0; index_cnt < mask_dq_num_of_regs;
|
|
index_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, interface_num,
|
|
mask_results_dq_reg_map[index_cnt],
|
|
(1 << 24), (1 << 24)));
|
|
}
|
|
}
|
|
|
|
/* Start Training Trigger */
|
|
CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, interface_num,
|
|
ODPG_TRAINING_TRIGGER_REG, 1, 1));
|
|
/* wait for all RFU tests to finish (or timeout) */
|
|
/* WA for 16 bit mode, more investigation needed */
|
|
mdelay(1);
|
|
|
|
/* Training "Done ?" */
|
|
for (index_cnt = 0; index_cnt < MAX_INTERFACE_NUM; index_cnt++) {
|
|
if (IS_ACTIVE(tm->if_act_mask, index_cnt) == 0)
|
|
continue;
|
|
|
|
if (interface_mask & (1 << index_cnt)) {
|
|
/* need to check results for this Dunit */
|
|
for (poll_cnt = 0; poll_cnt < max_polling_for_done;
|
|
poll_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_read
|
|
(dev_num, ACCESS_TYPE_UNICAST,
|
|
index_cnt,
|
|
ODPG_TRAINING_STATUS_REG,
|
|
®_data, MASK_ALL_BITS));
|
|
if ((reg_data & 0x2) != 0) {
|
|
/*done */
|
|
train_status[index_cnt] =
|
|
HWS_TRAINING_IP_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (poll_cnt == max_polling_for_done) {
|
|
train_status[index_cnt] =
|
|
HWS_TRAINING_IP_STATUS_TIMEOUT;
|
|
}
|
|
}
|
|
/* Be sure that ODPG done */
|
|
CHECK_STATUS(is_odpg_access_done(dev_num, index_cnt));
|
|
}
|
|
|
|
/* Write ODPG done in Dunit */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_STATUS_DONE_REG, 0, 0x1));
|
|
|
|
/* wait for all Dunit tests to finish (or timeout) */
|
|
/* Training "Done ?" */
|
|
/* Training "Pass ?" */
|
|
for (index_cnt = 0; index_cnt < MAX_INTERFACE_NUM; index_cnt++) {
|
|
if (IS_ACTIVE(tm->if_act_mask, index_cnt) == 0)
|
|
continue;
|
|
|
|
if (interface_mask & (1 << index_cnt)) {
|
|
/* need to check results for this Dunit */
|
|
for (poll_cnt = 0; poll_cnt < max_polling_for_done;
|
|
poll_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_read
|
|
(dev_num, ACCESS_TYPE_UNICAST,
|
|
index_cnt,
|
|
ODPG_TRAINING_TRIGGER_REG,
|
|
read_data, MASK_ALL_BITS));
|
|
reg_data = read_data[index_cnt];
|
|
if ((reg_data & 0x2) != 0) {
|
|
/* done */
|
|
if ((reg_data & 0x4) == 0) {
|
|
train_status[index_cnt] =
|
|
HWS_TRAINING_IP_STATUS_SUCCESS;
|
|
} else {
|
|
train_status[index_cnt] =
|
|
HWS_TRAINING_IP_STATUS_FAIL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (poll_cnt == max_polling_for_done) {
|
|
train_status[index_cnt] =
|
|
HWS_TRAINING_IP_STATUS_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_DATA_CONTROL_REG, 0, MASK_ALL_BITS));
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Load expected Pattern to ODPG
|
|
*/
|
|
int ddr3_tip_load_pattern_to_odpg(u32 dev_num, enum hws_access_type access_type,
|
|
u32 if_id, enum hws_pattern pattern,
|
|
u32 load_addr)
|
|
{
|
|
u32 pattern_length_cnt = 0;
|
|
struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
|
|
|
|
for (pattern_length_cnt = 0;
|
|
pattern_length_cnt < pattern_table[pattern].pattern_len;
|
|
pattern_length_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, if_id,
|
|
ODPG_PATTERN_DATA_LOW_REG,
|
|
pattern_table_get_word(dev_num, pattern,
|
|
(u8) (pattern_length_cnt *
|
|
2)), MASK_ALL_BITS));
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, if_id,
|
|
ODPG_PATTERN_DATA_HI_REG,
|
|
pattern_table_get_word(dev_num, pattern,
|
|
(u8) (pattern_length_cnt *
|
|
2 + 1)),
|
|
MASK_ALL_BITS));
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, if_id,
|
|
ODPG_PATTERN_ADDR_REG, pattern_length_cnt,
|
|
MASK_ALL_BITS));
|
|
}
|
|
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, access_type, if_id,
|
|
ODPG_PATTERN_ADDR_OFFSET_REG, load_addr, MASK_ALL_BITS));
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Configure ODPG
|
|
*/
|
|
int ddr3_tip_configure_odpg(u32 dev_num, enum hws_access_type access_type,
|
|
u32 if_id, enum hws_dir direction, u32 tx_phases,
|
|
u32 tx_burst_size, u32 rx_phases,
|
|
u32 delay_between_burst, u32 rd_mode, u32 cs_num,
|
|
u32 addr_stress_jump, u32 single_pattern)
|
|
{
|
|
u32 data_value = 0;
|
|
int ret;
|
|
|
|
data_value = ((single_pattern << 2) | (tx_phases << 5) |
|
|
(tx_burst_size << 11) | (delay_between_burst << 15) |
|
|
(rx_phases << 21) | (rd_mode << 25) | (cs_num << 26) |
|
|
(addr_stress_jump << 29));
|
|
ret = ddr3_tip_if_write(dev_num, access_type, if_id,
|
|
ODPG_DATA_CONTROL_REG, data_value, 0xaffffffc);
|
|
if (ret != MV_OK)
|
|
return ret;
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
int ddr3_tip_process_result(u32 *ar_result, enum hws_edge e_edge,
|
|
enum hws_edge_search e_edge_search,
|
|
u32 *edge_result)
|
|
{
|
|
u32 i, res;
|
|
int tap_val, max_val = -10000, min_val = 10000;
|
|
int lock_success = 1;
|
|
|
|
for (i = 0; i < BUS_WIDTH_IN_BITS; i++) {
|
|
res = GET_LOCK_RESULT(ar_result[i]);
|
|
if (res == 0) {
|
|
lock_success = 0;
|
|
break;
|
|
}
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("lock failed for bit %d\n", i));
|
|
}
|
|
|
|
if (lock_success == 1) {
|
|
for (i = 0; i < BUS_WIDTH_IN_BITS; i++) {
|
|
tap_val = GET_TAP_RESULT(ar_result[i], e_edge);
|
|
if (tap_val > max_val)
|
|
max_val = tap_val;
|
|
if (tap_val < min_val)
|
|
min_val = tap_val;
|
|
if (e_edge_search == TRAINING_EDGE_MAX)
|
|
*edge_result = (u32) max_val;
|
|
else
|
|
*edge_result = (u32) min_val;
|
|
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("i %d ar_result[i] 0x%x tap_val %d max_val %d min_val %d Edge_result %d\n",
|
|
i, ar_result[i], tap_val,
|
|
max_val, min_val,
|
|
*edge_result));
|
|
}
|
|
} else {
|
|
return MV_FAIL;
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Read training search result
|
|
*/
|
|
int ddr3_tip_read_training_result(u32 dev_num, u32 if_id,
|
|
enum hws_access_type pup_access_type,
|
|
u32 pup_num, u32 bit_num,
|
|
enum hws_search_dir search,
|
|
enum hws_dir direction,
|
|
enum hws_training_result result_type,
|
|
enum hws_training_load_op operation,
|
|
u32 cs_num_type, u32 **load_res,
|
|
int is_read_from_db, u8 cons_tap,
|
|
int is_check_result_validity)
|
|
{
|
|
u32 reg_offset, pup_cnt, start_pup, end_pup, start_reg, end_reg;
|
|
u32 *interface_train_res = NULL;
|
|
u16 *reg_addr = NULL;
|
|
u32 read_data[MAX_INTERFACE_NUM];
|
|
u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map();
|
|
u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg();
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
/*
|
|
* Agreed assumption: all CS mask contain same number of bits,
|
|
* i.e. in multi CS, the number of CS per memory is the same for
|
|
* all pups
|
|
*/
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_UNICAST, if_id, CS_ENABLE_REG,
|
|
(cs_num_type == 0) ? 1 << 3 : 0, (1 << 3)));
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_UNICAST, if_id,
|
|
ODPG_DATA_CONTROL_REG, (cs_num_type << 26), (3 << 26)));
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_TRACE,
|
|
("Read_from_d_b %d cs_type %d oper %d result_type %d direction %d search %d pup_num %d if_id %d pup_access_type %d\n",
|
|
is_read_from_db, cs_num_type, operation,
|
|
result_type, direction, search, pup_num,
|
|
if_id, pup_access_type));
|
|
|
|
if ((load_res == NULL) && (is_read_from_db == 1)) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("ddr3_tip_read_training_result load_res = NULL"));
|
|
return MV_FAIL;
|
|
}
|
|
if (pup_num >= tm->num_of_bus_per_interface) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("pup_num %d not valid\n", pup_num));
|
|
}
|
|
if (if_id >= MAX_INTERFACE_NUM) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("if_id %d not valid\n", if_id));
|
|
}
|
|
if (result_type == RESULT_PER_BIT)
|
|
reg_addr = mask_results_dq_reg_map;
|
|
else
|
|
reg_addr = mask_results_pup_reg_map;
|
|
if (pup_access_type == ACCESS_TYPE_UNICAST) {
|
|
start_pup = pup_num;
|
|
end_pup = pup_num;
|
|
} else { /*pup_access_type == ACCESS_TYPE_MULTICAST) */
|
|
|
|
start_pup = 0;
|
|
end_pup = tm->num_of_bus_per_interface - 1;
|
|
}
|
|
|
|
for (pup_cnt = start_pup; pup_cnt <= end_pup; pup_cnt++) {
|
|
VALIDATE_ACTIVE(tm->bus_act_mask, pup_cnt);
|
|
DEBUG_TRAINING_IP_ENGINE(
|
|
DEBUG_LEVEL_TRACE,
|
|
("if_id %d start_pup %d end_pup %d pup_cnt %d\n",
|
|
if_id, start_pup, end_pup, pup_cnt));
|
|
if (result_type == RESULT_PER_BIT) {
|
|
if (bit_num == ALL_BITS_PER_PUP) {
|
|
start_reg = pup_cnt * BUS_WIDTH_IN_BITS;
|
|
end_reg = (pup_cnt + 1) * BUS_WIDTH_IN_BITS - 1;
|
|
} else {
|
|
start_reg =
|
|
pup_cnt * BUS_WIDTH_IN_BITS + bit_num;
|
|
end_reg = pup_cnt * BUS_WIDTH_IN_BITS + bit_num;
|
|
}
|
|
} else {
|
|
start_reg = pup_cnt;
|
|
end_reg = pup_cnt;
|
|
}
|
|
|
|
interface_train_res =
|
|
ddr3_tip_get_buf_ptr(dev_num, search, result_type,
|
|
if_id);
|
|
DEBUG_TRAINING_IP_ENGINE(
|
|
DEBUG_LEVEL_TRACE,
|
|
("start_reg %d end_reg %d interface %p\n",
|
|
start_reg, end_reg, interface_train_res));
|
|
if (interface_train_res == NULL) {
|
|
DEBUG_TRAINING_IP_ENGINE(
|
|
DEBUG_LEVEL_ERROR,
|
|
("interface_train_res is NULL\n"));
|
|
return MV_FAIL;
|
|
}
|
|
|
|
for (reg_offset = start_reg; reg_offset <= end_reg;
|
|
reg_offset++) {
|
|
if (operation == TRAINING_LOAD_OPERATION_UNLOAD) {
|
|
if (is_read_from_db == 0) {
|
|
CHECK_STATUS(ddr3_tip_if_read
|
|
(dev_num,
|
|
ACCESS_TYPE_UNICAST,
|
|
if_id,
|
|
reg_addr[reg_offset],
|
|
read_data,
|
|
MASK_ALL_BITS));
|
|
if (is_check_result_validity == 1) {
|
|
if ((read_data[if_id] &
|
|
0x02000000) == 0) {
|
|
interface_train_res
|
|
[reg_offset] =
|
|
0x02000000 +
|
|
64 + cons_tap;
|
|
} else {
|
|
interface_train_res
|
|
[reg_offset] =
|
|
read_data
|
|
[if_id] +
|
|
cons_tap;
|
|
}
|
|
} else {
|
|
interface_train_res[reg_offset]
|
|
= read_data[if_id] +
|
|
cons_tap;
|
|
}
|
|
DEBUG_TRAINING_IP_ENGINE
|
|
(DEBUG_LEVEL_TRACE,
|
|
("reg_offset %d value 0x%x addr %p\n",
|
|
reg_offset,
|
|
interface_train_res
|
|
[reg_offset],
|
|
&interface_train_res
|
|
[reg_offset]));
|
|
} else {
|
|
*load_res =
|
|
&interface_train_res[start_reg];
|
|
DEBUG_TRAINING_IP_ENGINE
|
|
(DEBUG_LEVEL_TRACE,
|
|
("*load_res %p\n", *load_res));
|
|
}
|
|
} else {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_TRACE,
|
|
("not supported\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Load all pattern to memory using ODPG
|
|
*/
|
|
int ddr3_tip_load_all_pattern_to_mem(u32 dev_num)
|
|
{
|
|
u32 pattern = 0, if_id;
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, if_id);
|
|
training_result[training_stage][if_id] = TEST_SUCCESS;
|
|
}
|
|
|
|
for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, if_id);
|
|
/* enable single cs */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_UNICAST, if_id,
|
|
CS_ENABLE_REG, (1 << 3), (1 << 3)));
|
|
}
|
|
|
|
for (pattern = 0; pattern < PATTERN_LIMIT; pattern++)
|
|
ddr3_tip_load_pattern_to_mem(dev_num, pattern);
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Wait till ODPG access is ready
|
|
*/
|
|
int is_odpg_access_done(u32 dev_num, u32 if_id)
|
|
{
|
|
u32 poll_cnt = 0, data_value;
|
|
u32 read_data[MAX_INTERFACE_NUM];
|
|
|
|
for (poll_cnt = 0; poll_cnt < MAX_POLLING_ITERATIONS; poll_cnt++) {
|
|
CHECK_STATUS(ddr3_tip_if_read
|
|
(dev_num, ACCESS_TYPE_UNICAST, if_id,
|
|
ODPG_BIST_DONE, read_data, MASK_ALL_BITS));
|
|
data_value = read_data[if_id];
|
|
if (((data_value >> ODPG_BIST_DONE_BIT_OFFS) & 0x1) ==
|
|
ODPG_BIST_DONE_BIT_VALUE) {
|
|
data_value = data_value & 0xfffffffe;
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_UNICAST,
|
|
if_id, ODPG_BIST_DONE, data_value,
|
|
MASK_ALL_BITS));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (poll_cnt >= MAX_POLLING_ITERATIONS) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("Bist Activate: poll failure 2\n"));
|
|
return MV_FAIL;
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Load specific pattern to memory using ODPG
|
|
*/
|
|
int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern pattern)
|
|
{
|
|
u32 reg_data, if_id;
|
|
struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
/* load pattern to memory */
|
|
/*
|
|
* Write Tx mode, CS0, phases, Tx burst size, delay between burst,
|
|
* rx pattern phases
|
|
*/
|
|
reg_data =
|
|
0x1 | (pattern_table[pattern].num_of_phases_tx << 5) |
|
|
(pattern_table[pattern].tx_burst_size << 11) |
|
|
(pattern_table[pattern].delay_between_bursts << 15) |
|
|
(pattern_table[pattern].num_of_phases_rx << 21) | (0x1 << 25) |
|
|
(effective_cs << 26);
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_DATA_CONTROL_REG, reg_data, MASK_ALL_BITS));
|
|
/* ODPG Write enable from BIST */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_DATA_CONTROL_REG, (0x1 | (effective_cs << 26)),
|
|
0xc000003));
|
|
/* disable error injection */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_WRITE_DATA_ERROR_REG, 0, 0x1));
|
|
/* load pattern to ODPG */
|
|
ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST,
|
|
PARAM_NOT_CARE, pattern,
|
|
pattern_table[pattern].start_addr);
|
|
|
|
for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
|
|
if (IS_ACTIVE(tm->if_act_mask, if_id) == 0)
|
|
continue;
|
|
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_UNICAST, if_id, 0x1498,
|
|
0x3, 0xf));
|
|
}
|
|
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_ENABLE_REG, 0x1 << ODPG_ENABLE_OFFS,
|
|
(0x1 << ODPG_ENABLE_OFFS)));
|
|
|
|
mdelay(1);
|
|
|
|
for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, if_id);
|
|
CHECK_STATUS(is_odpg_access_done(dev_num, if_id));
|
|
}
|
|
|
|
/* Disable ODPG and stop write to memory */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_DATA_CONTROL_REG, (0x1 << 30), (u32) (0x3 << 30)));
|
|
|
|
/* return to default */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE,
|
|
ODPG_DATA_CONTROL_REG, 0, MASK_ALL_BITS));
|
|
|
|
/* Disable odt0 for CS0 training - need to adjust for multy CS */
|
|
CHECK_STATUS(ddr3_tip_if_write
|
|
(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x1498,
|
|
0x0, 0xf));
|
|
|
|
/* temporary added */
|
|
mdelay(1);
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Load specific pattern to memory using CPU
|
|
*/
|
|
int ddr3_tip_load_pattern_to_mem_by_cpu(u32 dev_num, enum hws_pattern pattern,
|
|
u32 offset)
|
|
{
|
|
/* eranba - TBD */
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Training search routine
|
|
*/
|
|
int ddr3_tip_ip_training_wrapper_int(u32 dev_num,
|
|
enum hws_access_type access_type,
|
|
u32 if_id,
|
|
enum hws_access_type pup_access_type,
|
|
u32 pup_num, u32 bit_num,
|
|
enum hws_training_result result_type,
|
|
enum hws_control_element control_element,
|
|
enum hws_search_dir search_dir,
|
|
enum hws_dir direction,
|
|
u32 interface_mask, u32 init_value_l2h,
|
|
u32 init_value_h2l, u32 num_iter,
|
|
enum hws_pattern pattern,
|
|
enum hws_edge_compare edge_comp,
|
|
enum hws_ddr_cs train_cs_type, u32 cs_num,
|
|
enum hws_training_ip_stat *train_status)
|
|
{
|
|
u32 interface_num = 0, start_if, end_if, init_value_used;
|
|
enum hws_search_dir search_dir_id, start_search, end_search;
|
|
enum hws_edge_compare edge_comp_used;
|
|
u8 cons_tap = (direction == OPER_WRITE) ? (64) : (0);
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
if (train_status == NULL) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("train_status is NULL\n"));
|
|
return MV_FAIL;
|
|
}
|
|
|
|
if ((train_cs_type > CS_NON_SINGLE) ||
|
|
(edge_comp >= EDGE_PFP) ||
|
|
(pattern >= PATTERN_LIMIT) ||
|
|
(direction > OPER_WRITE_AND_READ) ||
|
|
(search_dir > HWS_HIGH2LOW) ||
|
|
(control_element > HWS_CONTROL_ELEMENT_DQS_SKEW) ||
|
|
(result_type > RESULT_PER_BYTE) ||
|
|
(pup_num >= tm->num_of_bus_per_interface) ||
|
|
(pup_access_type > ACCESS_TYPE_MULTICAST) ||
|
|
(if_id > 11) || (access_type > ACCESS_TYPE_MULTICAST)) {
|
|
DEBUG_TRAINING_IP_ENGINE(
|
|
DEBUG_LEVEL_ERROR,
|
|
("wrong parameter train_cs_type %d edge_comp %d pattern %d direction %d search_dir %d control_element %d result_type %d pup_num %d pup_access_type %d if_id %d access_type %d\n",
|
|
train_cs_type, edge_comp, pattern, direction,
|
|
search_dir, control_element, result_type, pup_num,
|
|
pup_access_type, if_id, access_type));
|
|
return MV_FAIL;
|
|
}
|
|
|
|
if (edge_comp == EDGE_FPF) {
|
|
start_search = HWS_LOW2HIGH;
|
|
end_search = HWS_HIGH2LOW;
|
|
edge_comp_used = EDGE_FP;
|
|
} else {
|
|
start_search = search_dir;
|
|
end_search = search_dir;
|
|
edge_comp_used = edge_comp;
|
|
}
|
|
|
|
for (search_dir_id = start_search; search_dir_id <= end_search;
|
|
search_dir_id++) {
|
|
init_value_used = (search_dir_id == HWS_LOW2HIGH) ?
|
|
init_value_l2h : init_value_h2l;
|
|
DEBUG_TRAINING_IP_ENGINE(
|
|
DEBUG_LEVEL_TRACE,
|
|
("dev_num %d, access_type %d, if_id %d, pup_access_type %d,pup_num %d, result_type %d, control_element %d search_dir_id %d, direction %d, interface_mask %d,init_value_used %d, num_iter %d, pattern %d, edge_comp_used %d, train_cs_type %d, cs_num %d\n",
|
|
dev_num, access_type, if_id, pup_access_type, pup_num,
|
|
result_type, control_element, search_dir_id,
|
|
direction, interface_mask, init_value_used, num_iter,
|
|
pattern, edge_comp_used, train_cs_type, cs_num));
|
|
|
|
ddr3_tip_ip_training(dev_num, access_type, if_id,
|
|
pup_access_type, pup_num, result_type,
|
|
control_element, search_dir_id, direction,
|
|
interface_mask, init_value_used, num_iter,
|
|
pattern, edge_comp_used, train_cs_type,
|
|
cs_num, train_status);
|
|
if (access_type == ACCESS_TYPE_MULTICAST) {
|
|
start_if = 0;
|
|
end_if = MAX_INTERFACE_NUM - 1;
|
|
} else {
|
|
start_if = if_id;
|
|
end_if = if_id;
|
|
}
|
|
|
|
for (interface_num = start_if; interface_num <= end_if;
|
|
interface_num++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, interface_num);
|
|
cs_num = 0;
|
|
CHECK_STATUS(ddr3_tip_read_training_result
|
|
(dev_num, interface_num, pup_access_type,
|
|
pup_num, bit_num, search_dir_id,
|
|
direction, result_type,
|
|
TRAINING_LOAD_OPERATION_UNLOAD,
|
|
train_cs_type, NULL, 0, cons_tap,
|
|
0));
|
|
}
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Training search & read result routine
|
|
*/
|
|
int ddr3_tip_ip_training_wrapper(u32 dev_num, enum hws_access_type access_type,
|
|
u32 if_id,
|
|
enum hws_access_type pup_access_type,
|
|
u32 pup_num,
|
|
enum hws_training_result result_type,
|
|
enum hws_control_element control_element,
|
|
enum hws_search_dir search_dir,
|
|
enum hws_dir direction, u32 interface_mask,
|
|
u32 init_value_l2h, u32 init_value_h2l,
|
|
u32 num_iter, enum hws_pattern pattern,
|
|
enum hws_edge_compare edge_comp,
|
|
enum hws_ddr_cs train_cs_type, u32 cs_num,
|
|
enum hws_training_ip_stat *train_status)
|
|
{
|
|
u8 e1, e2;
|
|
u32 interface_cnt, bit_id, start_if, end_if, bit_end = 0;
|
|
u32 *result[HWS_SEARCH_DIR_LIMIT] = { 0 };
|
|
u8 cons_tap = (direction == OPER_WRITE) ? (64) : (0);
|
|
u8 bit_bit_mask[MAX_BUS_NUM] = { 0 }, bit_bit_mask_active = 0;
|
|
u8 pup_id;
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
if (pup_num >= tm->num_of_bus_per_interface) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("pup_num %d not valid\n", pup_num));
|
|
}
|
|
|
|
if (if_id >= MAX_INTERFACE_NUM) {
|
|
DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR,
|
|
("if_id %d not valid\n", if_id));
|
|
}
|
|
|
|
CHECK_STATUS(ddr3_tip_ip_training_wrapper_int
|
|
(dev_num, access_type, if_id, pup_access_type, pup_num,
|
|
ALL_BITS_PER_PUP, result_type, control_element,
|
|
search_dir, direction, interface_mask, init_value_l2h,
|
|
init_value_h2l, num_iter, pattern, edge_comp,
|
|
train_cs_type, cs_num, train_status));
|
|
|
|
if (access_type == ACCESS_TYPE_MULTICAST) {
|
|
start_if = 0;
|
|
end_if = MAX_INTERFACE_NUM - 1;
|
|
} else {
|
|
start_if = if_id;
|
|
end_if = if_id;
|
|
}
|
|
|
|
for (interface_cnt = start_if; interface_cnt <= end_if;
|
|
interface_cnt++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, interface_cnt);
|
|
for (pup_id = 0;
|
|
pup_id <= (tm->num_of_bus_per_interface - 1); pup_id++) {
|
|
VALIDATE_ACTIVE(tm->bus_act_mask, pup_id);
|
|
if (result_type == RESULT_PER_BIT)
|
|
bit_end = BUS_WIDTH_IN_BITS - 1;
|
|
else
|
|
bit_end = 0;
|
|
|
|
bit_bit_mask[pup_id] = 0;
|
|
for (bit_id = 0; bit_id <= bit_end; bit_id++) {
|
|
enum hws_search_dir search_dir_id;
|
|
for (search_dir_id = HWS_LOW2HIGH;
|
|
search_dir_id <= HWS_HIGH2LOW;
|
|
search_dir_id++) {
|
|
CHECK_STATUS
|
|
(ddr3_tip_read_training_result
|
|
(dev_num, interface_cnt,
|
|
ACCESS_TYPE_UNICAST, pup_id,
|
|
bit_id, search_dir_id,
|
|
direction, result_type,
|
|
TRAINING_LOAD_OPERATION_UNLOAD,
|
|
CS_SINGLE,
|
|
&result[search_dir_id],
|
|
1, 0, 0));
|
|
}
|
|
e1 = GET_TAP_RESULT(result[HWS_LOW2HIGH][0],
|
|
EDGE_1);
|
|
e2 = GET_TAP_RESULT(result[HWS_HIGH2LOW][0],
|
|
EDGE_1);
|
|
DEBUG_TRAINING_IP_ENGINE(
|
|
DEBUG_LEVEL_INFO,
|
|
("wrapper if_id %d pup_id %d bit %d l2h 0x%x (e1 0x%x) h2l 0x%x (e2 0x%x)\n",
|
|
interface_cnt, pup_id, bit_id,
|
|
result[HWS_LOW2HIGH][0], e1,
|
|
result[HWS_HIGH2LOW][0], e2));
|
|
/* TBD validate is valid only for tx */
|
|
if (VALIDATE_TRAINING_LIMIT(e1, e2) == 1 &&
|
|
GET_LOCK_RESULT(result[HWS_LOW2HIGH][0]) &&
|
|
GET_LOCK_RESULT(result[HWS_LOW2HIGH][0])) {
|
|
/* Mark problem bits */
|
|
bit_bit_mask[pup_id] |= 1 << bit_id;
|
|
bit_bit_mask_active = 1;
|
|
}
|
|
} /* For all bits */
|
|
} /* For all PUPs */
|
|
|
|
/* Fix problem bits */
|
|
if (bit_bit_mask_active != 0) {
|
|
u32 *l2h_if_train_res = NULL;
|
|
u32 *h2l_if_train_res = NULL;
|
|
l2h_if_train_res =
|
|
ddr3_tip_get_buf_ptr(dev_num, HWS_LOW2HIGH,
|
|
result_type,
|
|
interface_cnt);
|
|
h2l_if_train_res =
|
|
ddr3_tip_get_buf_ptr(dev_num, HWS_HIGH2LOW,
|
|
result_type,
|
|
interface_cnt);
|
|
|
|
ddr3_tip_ip_training(dev_num, ACCESS_TYPE_UNICAST,
|
|
interface_cnt,
|
|
ACCESS_TYPE_MULTICAST,
|
|
PARAM_NOT_CARE, result_type,
|
|
control_element, HWS_LOW2HIGH,
|
|
direction, interface_mask,
|
|
num_iter / 2, num_iter / 2,
|
|
pattern, EDGE_FP, train_cs_type,
|
|
cs_num, train_status);
|
|
|
|
for (pup_id = 0;
|
|
pup_id <= (tm->num_of_bus_per_interface - 1);
|
|
pup_id++) {
|
|
VALIDATE_ACTIVE(tm->bus_act_mask, pup_id);
|
|
|
|
if (bit_bit_mask[pup_id] == 0)
|
|
continue;
|
|
|
|
for (bit_id = 0; bit_id <= bit_end; bit_id++) {
|
|
if ((bit_bit_mask[pup_id] &
|
|
(1 << bit_id)) == 0)
|
|
continue;
|
|
CHECK_STATUS
|
|
(ddr3_tip_read_training_result
|
|
(dev_num, interface_cnt,
|
|
ACCESS_TYPE_UNICAST, pup_id,
|
|
bit_id, HWS_LOW2HIGH,
|
|
direction,
|
|
result_type,
|
|
TRAINING_LOAD_OPERATION_UNLOAD,
|
|
CS_SINGLE, &l2h_if_train_res,
|
|
0, 0, 1));
|
|
}
|
|
}
|
|
|
|
ddr3_tip_ip_training(dev_num, ACCESS_TYPE_UNICAST,
|
|
interface_cnt,
|
|
ACCESS_TYPE_MULTICAST,
|
|
PARAM_NOT_CARE, result_type,
|
|
control_element, HWS_HIGH2LOW,
|
|
direction, interface_mask,
|
|
num_iter / 2, num_iter / 2,
|
|
pattern, EDGE_FP, train_cs_type,
|
|
cs_num, train_status);
|
|
|
|
for (pup_id = 0;
|
|
pup_id <= (tm->num_of_bus_per_interface - 1);
|
|
pup_id++) {
|
|
VALIDATE_ACTIVE(tm->bus_act_mask, pup_id);
|
|
|
|
if (bit_bit_mask[pup_id] == 0)
|
|
continue;
|
|
|
|
for (bit_id = 0; bit_id <= bit_end; bit_id++) {
|
|
if ((bit_bit_mask[pup_id] &
|
|
(1 << bit_id)) == 0)
|
|
continue;
|
|
CHECK_STATUS
|
|
(ddr3_tip_read_training_result
|
|
(dev_num, interface_cnt,
|
|
ACCESS_TYPE_UNICAST, pup_id,
|
|
bit_id, HWS_HIGH2LOW, direction,
|
|
result_type,
|
|
TRAINING_LOAD_OPERATION_UNLOAD,
|
|
CS_SINGLE, &h2l_if_train_res,
|
|
0, cons_tap, 1));
|
|
}
|
|
}
|
|
} /* if bit_bit_mask_active */
|
|
} /* For all Interfacess */
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
/*
|
|
* Load phy values
|
|
*/
|
|
int ddr3_tip_load_phy_values(int b_load)
|
|
{
|
|
u32 bus_cnt = 0, if_id, dev_num = 0;
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, if_id);
|
|
for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES();
|
|
bus_cnt++) {
|
|
VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt);
|
|
if (b_load == 1) {
|
|
CHECK_STATUS(ddr3_tip_bus_read
|
|
(dev_num, if_id,
|
|
ACCESS_TYPE_UNICAST, bus_cnt,
|
|
DDR_PHY_DATA,
|
|
WRITE_CENTRALIZATION_PHY_REG +
|
|
(effective_cs *
|
|
CS_REGISTER_ADDR_OFFSET),
|
|
&phy_reg_bk[if_id][bus_cnt]
|
|
[0]));
|
|
CHECK_STATUS(ddr3_tip_bus_read
|
|
(dev_num, if_id,
|
|
ACCESS_TYPE_UNICAST, bus_cnt,
|
|
DDR_PHY_DATA,
|
|
RL_PHY_REG +
|
|
(effective_cs *
|
|
CS_REGISTER_ADDR_OFFSET),
|
|
&phy_reg_bk[if_id][bus_cnt]
|
|
[1]));
|
|
CHECK_STATUS(ddr3_tip_bus_read
|
|
(dev_num, if_id,
|
|
ACCESS_TYPE_UNICAST, bus_cnt,
|
|
DDR_PHY_DATA,
|
|
READ_CENTRALIZATION_PHY_REG +
|
|
(effective_cs *
|
|
CS_REGISTER_ADDR_OFFSET),
|
|
&phy_reg_bk[if_id][bus_cnt]
|
|
[2]));
|
|
} else {
|
|
CHECK_STATUS(ddr3_tip_bus_write
|
|
(dev_num, ACCESS_TYPE_UNICAST,
|
|
if_id, ACCESS_TYPE_UNICAST,
|
|
bus_cnt, DDR_PHY_DATA,
|
|
WRITE_CENTRALIZATION_PHY_REG +
|
|
(effective_cs *
|
|
CS_REGISTER_ADDR_OFFSET),
|
|
phy_reg_bk[if_id][bus_cnt]
|
|
[0]));
|
|
CHECK_STATUS(ddr3_tip_bus_write
|
|
(dev_num, ACCESS_TYPE_UNICAST,
|
|
if_id, ACCESS_TYPE_UNICAST,
|
|
bus_cnt, DDR_PHY_DATA,
|
|
RL_PHY_REG +
|
|
(effective_cs *
|
|
CS_REGISTER_ADDR_OFFSET),
|
|
phy_reg_bk[if_id][bus_cnt]
|
|
[1]));
|
|
CHECK_STATUS(ddr3_tip_bus_write
|
|
(dev_num, ACCESS_TYPE_UNICAST,
|
|
if_id, ACCESS_TYPE_UNICAST,
|
|
bus_cnt, DDR_PHY_DATA,
|
|
READ_CENTRALIZATION_PHY_REG +
|
|
(effective_cs *
|
|
CS_REGISTER_ADDR_OFFSET),
|
|
phy_reg_bk[if_id][bus_cnt]
|
|
[2]));
|
|
}
|
|
}
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
int ddr3_tip_training_ip_test(u32 dev_num, enum hws_training_result result_type,
|
|
enum hws_search_dir search_dir,
|
|
enum hws_dir direction,
|
|
enum hws_edge_compare edge,
|
|
u32 init_val1, u32 init_val2,
|
|
u32 num_of_iterations,
|
|
u32 start_pattern, u32 end_pattern)
|
|
{
|
|
u32 pattern, if_id, pup_id;
|
|
enum hws_training_ip_stat train_status[MAX_INTERFACE_NUM];
|
|
u32 *res = NULL;
|
|
u32 search_state = 0;
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
ddr3_tip_load_phy_values(1);
|
|
|
|
for (pattern = start_pattern; pattern <= end_pattern; pattern++) {
|
|
for (search_state = 0; search_state < HWS_SEARCH_DIR_LIMIT;
|
|
search_state++) {
|
|
ddr3_tip_ip_training_wrapper(dev_num,
|
|
ACCESS_TYPE_MULTICAST, 0,
|
|
ACCESS_TYPE_MULTICAST, 0,
|
|
result_type,
|
|
HWS_CONTROL_ELEMENT_ADLL,
|
|
search_dir, direction,
|
|
0xfff, init_val1,
|
|
init_val2,
|
|
num_of_iterations, pattern,
|
|
edge, CS_SINGLE,
|
|
PARAM_NOT_CARE,
|
|
train_status);
|
|
|
|
for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1;
|
|
if_id++) {
|
|
VALIDATE_ACTIVE(tm->if_act_mask, if_id);
|
|
for (pup_id = 0; pup_id <
|
|
tm->num_of_bus_per_interface;
|
|
pup_id++) {
|
|
VALIDATE_ACTIVE(tm->bus_act_mask,
|
|
pup_id);
|
|
CHECK_STATUS
|
|
(ddr3_tip_read_training_result
|
|
(dev_num, if_id,
|
|
ACCESS_TYPE_UNICAST, pup_id,
|
|
ALL_BITS_PER_PUP,
|
|
search_state,
|
|
direction, result_type,
|
|
TRAINING_LOAD_OPERATION_UNLOAD,
|
|
CS_SINGLE, &res, 1, 0,
|
|
0));
|
|
if (result_type == RESULT_PER_BYTE) {
|
|
DEBUG_TRAINING_IP_ENGINE
|
|
(DEBUG_LEVEL_INFO,
|
|
("search_state %d if_id %d pup_id %d 0x%x\n",
|
|
search_state, if_id,
|
|
pup_id, res[0]));
|
|
} else {
|
|
DEBUG_TRAINING_IP_ENGINE
|
|
(DEBUG_LEVEL_INFO,
|
|
("search_state %d if_id %d pup_id %d 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
|
search_state, if_id,
|
|
pup_id, res[0],
|
|
res[1], res[2],
|
|
res[3], res[4],
|
|
res[5], res[6],
|
|
res[7]));
|
|
}
|
|
}
|
|
} /* interface */
|
|
} /* search */
|
|
} /* pattern */
|
|
|
|
ddr3_tip_load_phy_values(0);
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
struct pattern_info *ddr3_tip_get_pattern_table()
|
|
{
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) == 0)
|
|
return pattern_table_32;
|
|
else
|
|
return pattern_table_16;
|
|
}
|
|
|
|
u16 *ddr3_tip_get_mask_results_dq_reg()
|
|
{
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))
|
|
return mask_results_dq_reg_map_pup3_ecc;
|
|
else
|
|
return mask_results_dq_reg_map;
|
|
}
|
|
|
|
u16 *ddr3_tip_get_mask_results_pup_reg_map()
|
|
{
|
|
struct hws_topology_map *tm = ddr3_get_topology_map();
|
|
|
|
if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))
|
|
return mask_results_pup_reg_map_pup3_ecc;
|
|
else
|
|
return mask_results_pup_reg_map;
|
|
}
|