mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-13 23:02:59 +00:00
54a08c4139
This syncs drivers/ddr/marvell/a38x/ with the master branch of repository
https://github.com/MarvellEmbeddedProcessors/mv-ddr-marvell.git
up to the commit "mv_ddr: a3700: Use the right size for memset to not overflow"
d5acc10c287e40cc2feeb28710b92e45c93c702c
This patch was created by following steps:
1. Replace all a38x files in U-Boot tree by files from upstream github
Marvell mv-ddr-marvell repository.
2. Run following command to omit portions not relevant for a38x, ddr3, and ddr4:
files=drivers/ddr/marvell/a38x/*
unifdef -m -UMV_DDR -UMV_DDR_ATF -UCONFIG_APN806 \
-UCONFIG_MC_STATIC -UCONFIG_MC_STATIC_PRINT -UCONFIG_PHY_STATIC \
-UCONFIG_PHY_STATIC_PRINT -UCONFIG_CUSTOMER_BOARD_SUPPORT \
-UCONFIG_A3700 -UA3900 -UA80X0 -UA70X0 -DCONFIG_ARMADA_38X -UCONFIG_ARMADA_39X \
-UCONFIG_64BIT $files
3. Manually change license to SPDX-License-Identifier
(upstream license in upstream github repository contains long license
texts and U-Boot is using just SPDX-License-Identifier.
After applying this patch, a38x, ddr3, and ddr4 code in upstream Marvell github
repository and in U-Boot would be fully identical. So in future applying
above steps could be used to sync code again.
The only change in this patch are:
1. Some fixes with include files.
2. Some function return and basic type defines changes in
mv_ddr_plat.c (to correct Marvell bug).
3. Remove of dead code in newly copied files (as a result of the
filter script stripping out everything other than a38x, dd3, and ddr4).
Reference:
"ddr: marvell: a38x: Sync code with Marvell mv-ddr-marvell repository"
107c3391b9
Signed-off-by: Tony Dinh <mibodhi@gmail.com>
Reviewed-by: Pali Rohár <pali@kernel.org>
Reviewed-by: Stefan Roese <sr@denx.de>
441 lines
15 KiB
C
441 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) Marvell International Ltd. and its affiliates
|
|
*/
|
|
|
|
#if defined(CONFIG_DDR4)
|
|
|
|
#include "ddr3_init.h"
|
|
#include "mv_ddr_regs.h"
|
|
|
|
static int mv_ddr4_dynamic_pb_wl_supp(u32 dev_num, enum mv_wl_supp_mode ecc_mode);
|
|
|
|
/* compare test for ddr4 write leveling supplementary */
|
|
#define MV_DDR4_COMP_TEST_NO_RESULT 0
|
|
#define MV_DDR4_COMP_TEST_RESULT_0 1
|
|
#define MV_DDR4_XSB_COMP_PATTERNS_NUM 8
|
|
|
|
static u8 mv_ddr4_xsb_comp_test(u32 dev_num, u32 subphy_num, u32 if_id,
|
|
enum mv_wl_supp_mode ecc_mode)
|
|
{
|
|
u32 wl_invert;
|
|
u8 pb_key, bit, bit_max, word;
|
|
struct pattern_info *pattern_table = ddr3_tip_get_pattern_table();
|
|
struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
|
|
u32 subphy_max = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE);
|
|
uint64_t read_pattern_64[MV_DDR4_XSB_COMP_PATTERNS_NUM] = {0};
|
|
/*
|
|
* FIXME: the pattern below is used for writing to the memory
|
|
* by the cpu. it was changed to be written through the odpg.
|
|
* for a workaround
|
|
* uint64_t pattern_test_table_64[MV_DDR4_XSB_COMP_PATTERNS_NUM] = {
|
|
* 0xffffffffffffffff,
|
|
* 0xffffffffffffffff,
|
|
* 0x0000000000000000,
|
|
* 0x0000000000000000,
|
|
* 0x0000000000000000,
|
|
* 0x0000000000000000,
|
|
* 0xffffffffffffffff,
|
|
* 0xffffffffffffffff};
|
|
*/
|
|
u32 read_pattern[MV_DDR4_XSB_COMP_PATTERNS_NUM];
|
|
/*u32 pattern_test_table[MV_DDR4_XSB_COMP_PATTERNS_NUM] = {
|
|
0xffffffff,
|
|
0xffffffff,
|
|
0x00000000,
|
|
0x00000000,
|
|
0x00000000,
|
|
0x00000000,
|
|
0xffffffff,
|
|
0xffffffff}; TODO: use pattern_table_get_word */
|
|
int i, status;
|
|
uint64_t data64;
|
|
uintptr_t addr64;
|
|
int ecc_running = 0;
|
|
u32 ecc_read_subphy_num = 0; /* FIXME: change ecc read subphy num to be configurable */
|
|
u8 bit_counter = 0;
|
|
int edge = 0;
|
|
/* write and read data */
|
|
if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask)) {
|
|
status = ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ODPG_DATA_CTRL_REG,
|
|
effective_cs << ODPG_DATA_CS_OFFS,
|
|
ODPG_DATA_CS_MASK << ODPG_DATA_CS_OFFS);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
addr64 = (uintptr_t)pattern_table[PATTERN_TEST].start_addr;
|
|
/*
|
|
* FIXME: changed the load pattern to memory through the odpg
|
|
* this change is needed to be validate
|
|
* this change is done due to un calibrated dm at this stage
|
|
* the below code is the code for loading the pattern directly
|
|
* to the memory
|
|
*
|
|
* for (i = 0; i < MV_DDR4_XSB_COMP_PATTERNS_NUM; i++) {
|
|
* data64 = pattern_test_table_64[i];
|
|
* writeq(addr64, data64);
|
|
* addr64 += sizeof(uint64_t);
|
|
*}
|
|
* FIXME: the below code loads the pattern to the memory through the odpg
|
|
* it loads it twice to due supplementary failure, need to check it
|
|
*/
|
|
int j;
|
|
for (j = 0; j < 2; j++)
|
|
ddr3_tip_load_pattern_to_mem(dev_num, PATTERN_TEST);
|
|
|
|
} else if (MV_DDR_IS_32BIT_IN_64BIT_DRAM_MODE(tm->bus_act_mask, subphy_max)) {
|
|
status = ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ODPG_DATA_CTRL_REG,
|
|
effective_cs << ODPG_DATA_CS_OFFS,
|
|
ODPG_DATA_CS_MASK << ODPG_DATA_CS_OFFS);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/*
|
|
* FIXME: changed the load pattern to memory through the odpg
|
|
* this change is needed to be validate
|
|
* this change is done due to un calibrated dm at this stage
|
|
* the below code is the code for loading the pattern directly
|
|
* to the memory
|
|
*/
|
|
int j;
|
|
for (j = 0; j < 2; j++)
|
|
ddr3_tip_load_pattern_to_mem(dev_num, PATTERN_TEST);
|
|
} else {
|
|
/*
|
|
* FIXME: changed the load pattern to memory through the odpg
|
|
* this change is needed to be validate
|
|
* this change is done due to un calibrated dm at this stage
|
|
* the below code is the code for loading the pattern directly
|
|
* to the memory
|
|
*/
|
|
int j;
|
|
for (j = 0; j < 2; j++)
|
|
ddr3_tip_load_pattern_to_mem(dev_num, PATTERN_TEST);
|
|
}
|
|
|
|
if ((ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP4) ||
|
|
(ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP3 ||
|
|
ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8)) {
|
|
/* disable ecc write mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x0, 0x100);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* enable read data ecc mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x3, 0x3);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* unset training start bit */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_REG, 0x80000000, 0x80000000);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
ecc_running = 1;
|
|
ecc_read_subphy_num = ECC_READ_BUS_0;
|
|
}
|
|
|
|
if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask)) {
|
|
status = ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ODPG_DATA_CTRL_REG,
|
|
effective_cs << ODPG_DATA_CS_OFFS,
|
|
ODPG_DATA_CS_MASK << ODPG_DATA_CS_OFFS);
|
|
if (status != MV_OK)
|
|
return status;
|
|
/*
|
|
* in case of reading the pattern read it from the address x 8
|
|
* the odpg multiply by 8 the addres to read from
|
|
*/
|
|
addr64 = ((uintptr_t)pattern_table[PATTERN_TEST].start_addr) << 3;
|
|
for (i = 0; i < MV_DDR4_XSB_COMP_PATTERNS_NUM; i++) {
|
|
data64 = readq(addr64);
|
|
addr64 += sizeof(uint64_t);
|
|
read_pattern_64[i] = data64;
|
|
}
|
|
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("xsb comp: if %d bus id %d\n", 0, subphy_num));
|
|
for (edge = 0; edge < 8; edge++)
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("0x%16llx\n", (unsigned long long)read_pattern_64[edge]));
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("\n"));
|
|
} else if (MV_DDR_IS_32BIT_IN_64BIT_DRAM_MODE(tm->bus_act_mask, subphy_max)) {
|
|
status = ddr3_tip_if_write(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ODPG_DATA_CTRL_REG,
|
|
effective_cs << ODPG_DATA_CS_OFFS,
|
|
ODPG_DATA_CS_MASK << ODPG_DATA_CS_OFFS);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
status = ddr3_tip_ext_read(dev_num, if_id, pattern_table[PATTERN_TEST].start_addr << 3,
|
|
1, read_pattern);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("xsb comp: if %d bus id %d\n", 0, subphy_num));
|
|
for (edge = 0; edge < 8; edge++)
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("0x%16x\n", read_pattern[edge]));
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("\n"));
|
|
} else {
|
|
status = ddr3_tip_ext_read(dev_num, if_id, ((pattern_table[PATTERN_TEST].start_addr << 3) +
|
|
((SDRAM_CS_SIZE + 1) * effective_cs)), 1, read_pattern);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("xsb comp: if %d bus id %d\n", 0, subphy_num));
|
|
for (edge = 0; edge < 8; edge++)
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("0x%16x\n", read_pattern[edge]));
|
|
DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("\n"));
|
|
}
|
|
|
|
/* read centralization result to decide on half phase by inverse bit */
|
|
status = ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, subphy_num, DDR_PHY_DATA,
|
|
CTX_PHY_REG(0), &wl_invert);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
if ((wl_invert & 0x20) != 0)
|
|
wl_invert = 1;
|
|
else
|
|
wl_invert = 0;
|
|
|
|
/* for ecc, read from the "read" subphy (usualy subphy 0) */
|
|
if (ecc_running)
|
|
subphy_num = ecc_read_subphy_num;
|
|
|
|
/* per bit loop*/
|
|
bit_max = subphy_num * BUS_WIDTH_IN_BITS + BUS_WIDTH_IN_BITS;
|
|
for (bit = subphy_num * BUS_WIDTH_IN_BITS; bit < bit_max; bit++) {
|
|
/* get per bit pattern key (value of the same bit in the pattern) */
|
|
pb_key = 0;
|
|
for (word = 0; word < MV_DDR4_XSB_COMP_PATTERNS_NUM; word++) {
|
|
if (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask)) {
|
|
if ((read_pattern_64[word] & ((uint64_t)1 << bit)) != 0)
|
|
pb_key |= (1 << word);
|
|
} else {
|
|
if ((read_pattern[word] & (1 << bit)) != 0)
|
|
pb_key |= (1 << word);
|
|
}
|
|
}
|
|
|
|
/* find the key value and make decision */
|
|
switch (pb_key) {
|
|
/* case(s) for 0 */
|
|
case 0b11000011: /* nominal */
|
|
case 0b10000011: /* sample at start of UI sample at the dqvref TH */
|
|
case 0b10000111: /* sample at start of UI sample at the dqvref TH */
|
|
case 0b11000001: /* sample at start of UI sample at the dqvref TH */
|
|
case 0b11100001: /* sample at start of UI sample at the dqvref TH */
|
|
case 0b11100011: /* sample at start of UI sample at the dqvref TH */
|
|
case 0b11000111: /* sample at start of UI sample at the dqvref TH */
|
|
bit_counter++;
|
|
break;
|
|
} /* end of switch */
|
|
} /* end of per bit loop */
|
|
|
|
/* check all bits in the current subphy has met the switch condition above */
|
|
if (bit_counter == BUS_WIDTH_IN_BITS)
|
|
return MV_DDR4_COMP_TEST_RESULT_0;
|
|
else {
|
|
DEBUG_LEVELING(
|
|
DEBUG_LEVEL_INFO,
|
|
("different supplementary results (%d -> %d)\n",
|
|
MV_DDR4_COMP_TEST_NO_RESULT, MV_DDR4_COMP_TEST_RESULT_0));
|
|
return MV_DDR4_COMP_TEST_NO_RESULT;
|
|
}
|
|
}
|
|
|
|
int mv_ddr4_dynamic_wl_supp(u32 dev_num)
|
|
{
|
|
int status = MV_OK;
|
|
struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
|
|
|
|
if (DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask) ||
|
|
DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask) ||
|
|
DDR3_IS_ECC_PUP8_MODE(tm->bus_act_mask)) {
|
|
if (DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask))
|
|
status = mv_ddr4_dynamic_pb_wl_supp(dev_num, WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP4);
|
|
else if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))
|
|
status = mv_ddr4_dynamic_pb_wl_supp(dev_num, WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP3);
|
|
else /* WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8 */
|
|
status = mv_ddr4_dynamic_pb_wl_supp(dev_num, WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8);
|
|
if (status != MV_OK)
|
|
return status;
|
|
status = mv_ddr4_dynamic_pb_wl_supp(dev_num, WRITE_LEVELING_SUPP_ECC_MODE_DATA_PUPS);
|
|
} else { /* regular supplementary for data subphys in non-ecc mode */
|
|
status = mv_ddr4_dynamic_pb_wl_supp(dev_num, WRITE_LEVELING_SUPP_REG_MODE);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* dynamic per bit write leveling supplementary */
|
|
static int mv_ddr4_dynamic_pb_wl_supp(u32 dev_num, enum mv_wl_supp_mode ecc_mode)
|
|
{
|
|
u32 if_id;
|
|
u32 subphy_start, subphy_end;
|
|
u32 subphy_num = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE);
|
|
u8 compare_result = 0;
|
|
u32 orig_phase;
|
|
u32 rd_data, wr_data = 0;
|
|
u32 flag, step;
|
|
struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
|
|
u32 ecc_phy_access_id;
|
|
int status;
|
|
|
|
if (ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP4 ||
|
|
ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP3 ||
|
|
ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8) {
|
|
/* enable ecc write mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x100, 0x100);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* disable read data ecc mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x0, 0x3);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* unset training start bit */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_REG, 0x0, 0x80000000);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
if (ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP3)
|
|
ecc_phy_access_id = ECC_PHY_ACCESS_3;
|
|
else if (ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP4)
|
|
ecc_phy_access_id = ECC_PHY_ACCESS_4;
|
|
else /* ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8 */
|
|
ecc_phy_access_id = ECC_PHY_ACCESS_8;
|
|
|
|
subphy_start = ecc_phy_access_id;
|
|
subphy_end = subphy_start + 1;
|
|
} else if (ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_DATA_PUPS) {
|
|
/* disable ecc write mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x0, 0x100);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* disable ecc mode*/
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
SDRAM_CFG_REG, 0, 0x40000);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
subphy_start = 0;
|
|
if (MV_DDR_IS_HALF_BUS_DRAM_MODE(tm->bus_act_mask, subphy_num))
|
|
subphy_end = (subphy_num - 1) / 2;
|
|
else
|
|
subphy_end = subphy_num - 1;
|
|
} else { /* ecc_mode == WRITE_LEVELING_SUPP_REG_MODE */
|
|
subphy_start = 0;
|
|
/* remove ecc subphy prior to algorithm's start */
|
|
subphy_end = subphy_num - 1; /* TODO: check it */
|
|
}
|
|
|
|
for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) {
|
|
VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id);
|
|
for (subphy_num = subphy_start; subphy_num < subphy_end; subphy_num++) {
|
|
VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num);
|
|
flag = 1;
|
|
step = 0;
|
|
status = ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, subphy_num, DDR_PHY_DATA,
|
|
WL_PHY_REG(effective_cs), &rd_data);
|
|
if (status != MV_OK)
|
|
return status;
|
|
orig_phase = (rd_data >> 6) & 0x7;
|
|
while (flag != 0) {
|
|
/* get decision for subphy */
|
|
compare_result = mv_ddr4_xsb_comp_test(dev_num, subphy_num, if_id, ecc_mode);
|
|
if (compare_result == MV_DDR4_COMP_TEST_RESULT_0) {
|
|
flag = 0;
|
|
} else { /* shift phase to -1 */
|
|
step++;
|
|
if (step == 1) { /* set phase (0x0[6-8]) to -2 */
|
|
if (orig_phase > 1)
|
|
wr_data = (rd_data & ~0x1c0) | ((orig_phase - 2) << 6);
|
|
else if (orig_phase == 1)
|
|
wr_data = (rd_data & ~0x1df);
|
|
if (orig_phase >= 1)
|
|
ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
|
|
ACCESS_TYPE_UNICAST, subphy_num,
|
|
DDR_PHY_DATA,
|
|
WL_PHY_REG(effective_cs), wr_data);
|
|
} else if (step == 2) { /* shift phase to +1 */
|
|
if (orig_phase <= 5) {
|
|
wr_data = (rd_data & ~0x1c0) | ((orig_phase + 2) << 6);
|
|
ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
|
|
ACCESS_TYPE_UNICAST, subphy_num,
|
|
DDR_PHY_DATA,
|
|
WL_PHY_REG(effective_cs), wr_data);
|
|
}
|
|
} else if (step == 3) {
|
|
if (orig_phase <= 3) {
|
|
wr_data = (rd_data & ~0x1c0) | ((orig_phase + 4) << 6);
|
|
ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id,
|
|
ACCESS_TYPE_UNICAST, subphy_num,
|
|
DDR_PHY_DATA,
|
|
WL_PHY_REG(effective_cs), wr_data);
|
|
}
|
|
} else { /* error */
|
|
flag = 0;
|
|
compare_result = MV_DDR4_COMP_TEST_NO_RESULT;
|
|
training_result[training_stage][if_id] = TEST_FAILED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ((training_result[training_stage][if_id] == NO_TEST_DONE) ||
|
|
(training_result[training_stage][if_id] == TEST_SUCCESS))
|
|
training_result[training_stage][if_id] = TEST_SUCCESS;
|
|
}
|
|
|
|
if (ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_DATA_PUPS) {
|
|
/* enable ecc write mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x100, 0x100);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* enable ecc mode*/
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
SDRAM_CFG_REG, 0x40000, 0x40000);
|
|
if (status != MV_OK)
|
|
return status;
|
|
} else if (ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP4 ||
|
|
ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP3 ||
|
|
ecc_mode == WRITE_LEVELING_SUPP_ECC_MODE_ECC_PUP8) {
|
|
/* enable ecc write mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x100, 0x100);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* disable read data ecc mux */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_2_REG, 0x0, 0x3);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
/* unset training start bit */
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_REG, 0x0, 0x80000000);
|
|
if (status != MV_OK)
|
|
return status;
|
|
|
|
status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE,
|
|
TRAINING_SW_1_REG, 0x1 << 16, 0x1 << 16);
|
|
if (status != MV_OK)
|
|
return status;
|
|
} else {
|
|
/* do nothing for WRITE_LEVELING_SUPP_REG_MODE */;
|
|
}
|
|
if (training_result[training_stage][0] == TEST_SUCCESS)
|
|
return MV_OK;
|
|
else
|
|
return MV_FAIL;
|
|
}
|
|
#endif /* CONFIG_DDR4 */
|