diff options
author | Tony Dinh <mibodhi@gmail.com> | 2023-01-18 19:03:04 -0800 |
---|---|---|
committer | Stefan Roese <sr@denx.de> | 2023-01-26 07:30:20 +0100 |
commit | 54a08c4139e6677494d62c7cb595d70ef123a86b (patch) | |
tree | 1e5ee0e5183844df43a8d968e61aa2487a9856e2 /drivers/ddr/marvell/a38x/mv_ddr4_training_calibration.c | |
parent | 17e8e58fe62c019b2cc26af221b6defc3368229f (diff) |
ddr: marvell: a38x: Add support for DDR4 from Marvell mv-ddr-marvell repository
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"
https://source.denx.de/u-boot/u-boot/-/commit/107c3391b95bcc2ba09a876da4fa0c31b6c1e460
Signed-off-by: Tony Dinh <mibodhi@gmail.com>
Reviewed-by: Pali Rohár <pali@kernel.org>
Reviewed-by: Stefan Roese <sr@denx.de>
Diffstat (limited to 'drivers/ddr/marvell/a38x/mv_ddr4_training_calibration.c')
-rw-r--r-- | drivers/ddr/marvell/a38x/mv_ddr4_training_calibration.c | 2336 |
1 files changed, 2336 insertions, 0 deletions
diff --git a/drivers/ddr/marvell/a38x/mv_ddr4_training_calibration.c b/drivers/ddr/marvell/a38x/mv_ddr4_training_calibration.c new file mode 100644 index 0000000000..31b6209416 --- /dev/null +++ b/drivers/ddr/marvell/a38x/mv_ddr4_training_calibration.c @@ -0,0 +1,2336 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Marvell International Ltd. and its affiliates + */ + +#if defined(CONFIG_DDR4) + +/* DESCRIPTION: DDR4 Receiver and DQVref Calibration */ + +#include "ddr3_init.h" +#include "mv_ddr4_training_calibration.h" +#include "mv_ddr4_training.h" +#include "mv_ddr4_mpr_pda_if.h" +#include "mv_ddr_training_db.h" +#include "mv_ddr_regs.h" + +#define RX_DIR 0 +#define TX_DIR 1 +#define MAX_DIR_TYPES 2 + +#define RECEIVER_DC_STEP_SIZE 3 +#define RECEIVER_DC_MIN_RANGE 0 +#define RECEIVER_DC_MAX_RANGE 63 +#define RECEIVER_DC_MAX_COUNT (((RECEIVER_DC_MAX_RANGE - RECEIVER_DC_MIN_RANGE) / RECEIVER_DC_STEP_SIZE) + 1) + +#define PBS_VAL_FACTOR 1000 +#define MV_DDR_VW_TX_NOISE_FILTER 8 /* adlls */ + +u8 dq_vref_vec[MAX_BUS_NUM]; /* stability support */ +u8 rx_eye_hi_lvl[MAX_BUS_NUM]; /* rx adjust support */ +u8 rx_eye_lo_lvl[MAX_BUS_NUM]; /* rx adjust support */ + +static u8 pbs_max = 31; +static u8 vdq_tv; /* vref value for dq vref calibration */ +static u8 duty_cycle; /* duty cycle value for receiver calibration */ +static u8 rx_vw_pos[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +static u8 patterns_byte_status[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +static const char *str_dir[MAX_DIR_TYPES] = {"read", "write"}; + +static u8 center_low_element_get(u8 dir, u8 pbs_element, u16 lambda, u8 pbs_max_val) +{ + u8 result; + + if (dir == RX_DIR) + result = pbs_element * lambda / PBS_VAL_FACTOR; + else + result = (pbs_max_val - pbs_element) * lambda / PBS_VAL_FACTOR; + + return result; +} + +static u8 center_high_element_get(u8 dir, u8 pbs_element, u16 lambda, u8 pbs_max_val) +{ + u8 result; + + if (dir == RX_DIR) + result = (pbs_max_val - pbs_element) * lambda / PBS_VAL_FACTOR; + else + result = pbs_element * lambda / PBS_VAL_FACTOR; + + return result; +} + +static int mv_ddr4_centralization(u8 dev_num, u16 (*lambda)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS], u8 (*copt)[MAX_BUS_NUM], + u8 (*pbs_result)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS], u8 (*vw_size)[MAX_BUS_NUM], + u8 mode, u16 param0, u8 param1); +static int mv_ddr4_dqs_reposition(u8 dir, u16 *lambda, u8 *pbs_result, char delta, u8 *copt, u8 *dqs_pbs); +static int mv_ddr4_copt_get(u8 dir, u16 *lambda, u8 *vw_l, u8 *vw_h, u8 *pbs_result, u8 *copt); +static int mv_ddr4_center_of_mass_calc(u8 dev_num, u8 if_id, u8 subphy_num, u8 mode, u8 *vw_l, u8 *vw_h, u8 *vw_v, + u8 vw_num, u8 *v_opt, u8 *t_opt); +static int mv_ddr4_tap_tuning(u8 dev_num, u16 (*pbs_tap_factor)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS], u8 mode); + +/* dq vref calibration flow */ +int mv_ddr4_dq_vref_calibration(u8 dev_num, u16 (*pbs_tap_factor)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS]) +{ + u32 if_id, subphy_num; + u32 vref_idx, dq_idx, pad_num = 0; + u8 dq_vref_start_win[MAX_INTERFACE_NUM][MAX_BUS_NUM][MV_DDR4_VREF_MAX_COUNT]; + u8 dq_vref_end_win[MAX_INTERFACE_NUM][MAX_BUS_NUM][MV_DDR4_VREF_MAX_COUNT]; + u8 valid_win_size[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 c_opt_per_bus[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 valid_vref_cnt[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 valid_vref_ptr[MAX_INTERFACE_NUM][MAX_BUS_NUM][MV_DDR4_VREF_MAX_COUNT]; + u8 center_adll[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 center_vref[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 pbs_res_per_bus[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + u16 vref_avg, vref_subphy_num; + int vref_tap_idx; + int vref_range_min; + struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get(); + enum mv_ddr4_vref_subphy_cal_state all_subphys_state = MV_DDR4_VREF_SUBPHY_CAL_ABOVE; + int tap_tune_passed = 0; + enum mv_ddr4_vref_tap_state vref_tap_set_state = MV_DDR4_VREF_TAP_START; + enum hws_result *flow_result = ddr3_tip_get_result_ptr(training_stage); + u8 subphy_max = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE); + enum mv_ddr4_vref_subphy_cal_state vref_state_per_subphy[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + int status; + static u8 vref_byte_status[MAX_INTERFACE_NUM][MAX_BUS_NUM][MV_DDR4_VREF_MAX_RANGE]; + + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, ("Starting ddr4 dq vref calibration training stage\n")); + + vdq_tv = 0; + duty_cycle = 0; + + /* reset valid vref counter per if and subphy */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + for (subphy_num = 0; subphy_num < MAX_BUS_NUM; subphy_num++) { + valid_vref_cnt[if_id][subphy_num] = 0; + vref_state_per_subphy[if_id][subphy_num] = MV_DDR4_VREF_SUBPHY_CAL_ABOVE; + } + } + + if (mv_ddr4_tap_tuning(dev_num, pbs_tap_factor, TX_DIR) == MV_OK) + tap_tune_passed = 1; + + /* place dram to vref training mode */ + mv_ddr4_vref_training_mode_ctrl(dev_num, 0, ACCESS_TYPE_MULTICAST, 1); + + /* main loop for 2d scan (low_to_high voltage scan) */ + vref_tap_idx = MV_DDR4_VREF_MAX_RANGE; + vref_range_min = MV_DDR4_VREF_MIN_RANGE; + + if (vref_range_min < MV_DDR4_VREF_STEP_SIZE) + vref_range_min = MV_DDR4_VREF_STEP_SIZE; + + /* clean vref status array */ + memset(vref_byte_status, BYTE_NOT_DEFINED, sizeof(vref_byte_status)); + + for (vref_tap_idx = MV_DDR4_VREF_MAX_RANGE; (vref_tap_idx >= vref_range_min) && + (all_subphys_state != MV_DDR4_VREF_SUBPHY_CAL_UNDER); + vref_tap_idx -= MV_DDR4_VREF_STEP_SIZE) { + /* set new vref training value in dram */ + mv_ddr4_vref_tap_set(dev_num, 0, ACCESS_TYPE_MULTICAST, vref_tap_idx, vref_tap_set_state); + + if (tap_tune_passed == 0) { + if (mv_ddr4_tap_tuning(dev_num, pbs_tap_factor, TX_DIR) == MV_OK) + tap_tune_passed = 1; + else + continue; + } + + if (mv_ddr4_centralization(dev_num, pbs_tap_factor, c_opt_per_bus, pbs_res_per_bus, + valid_win_size, TX_DIR, vref_tap_idx, 0) != MV_OK) { + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("error: %s: ddr4 centralization failed (dq vref tap index %d)!!!\n", + __func__, vref_tap_idx)); + continue; + } + + /* go over all results and find out the vref start and end window */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + if (valid_win_size[if_id][subphy_num] > MV_DDR_VW_TX_NOISE_FILTER) { + if (vref_state_per_subphy[if_id][subphy_num] == MV_DDR4_VREF_SUBPHY_CAL_UNDER) + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("warning: %s: subphy %d vref tap %d voltage noise\n", + __func__, subphy_num, vref_tap_idx)); + /* window is valid; keep current vref_tap_idx value and increment counter */ + vref_idx = valid_vref_cnt[if_id][subphy_num]; + valid_vref_ptr[if_id][subphy_num][vref_idx] = vref_tap_idx; + valid_vref_cnt[if_id][subphy_num]++; + + /* set 0 for possible negative values */ + vref_byte_status[if_id][subphy_num][vref_idx] |= + patterns_byte_status[if_id][subphy_num]; + dq_vref_start_win[if_id][subphy_num][vref_idx] = + c_opt_per_bus[if_id][subphy_num] + 1 - + valid_win_size[if_id][subphy_num] / 2; + dq_vref_start_win[if_id][subphy_num][vref_idx] = + (valid_win_size[if_id][subphy_num] % 2 == 0) ? + dq_vref_start_win[if_id][subphy_num][vref_idx] : + dq_vref_start_win[if_id][subphy_num][vref_idx] - 1; + dq_vref_end_win[if_id][subphy_num][vref_idx] = + c_opt_per_bus[if_id][subphy_num] + + valid_win_size[if_id][subphy_num] / 2; + vref_state_per_subphy[if_id][subphy_num] = MV_DDR4_VREF_SUBPHY_CAL_INSIDE; + } else if (vref_state_per_subphy[if_id][subphy_num] == MV_DDR4_VREF_SUBPHY_CAL_INSIDE) { + vref_state_per_subphy[if_id][subphy_num] = MV_DDR4_VREF_SUBPHY_CAL_UNDER; + } + } /* subphy */ + } /* if */ + + /* check all subphys are in under state */ + all_subphys_state = MV_DDR4_VREF_SUBPHY_CAL_UNDER; + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + if (vref_state_per_subphy[if_id][subphy_num] != MV_DDR4_VREF_SUBPHY_CAL_UNDER) + all_subphys_state = MV_DDR4_VREF_SUBPHY_CAL_INSIDE; + } + } + } + + if (tap_tune_passed == 0) { + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("%s: tap tune not passed on any dq_vref value\n", __func__)); + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + /* report fail for all active interfaces; multi-interface support - tbd */ + flow_result[if_id] = TEST_FAILED; + } + + return MV_FAIL; + } + + /* close vref range */ + mv_ddr4_vref_tap_set(dev_num, 0, ACCESS_TYPE_MULTICAST, vref_tap_idx, MV_DDR4_VREF_TAP_END); + + /* + * find out the results with the mixed and low states and move the low state 64 adlls in case + * the center of the ui is smaller than 31 + */ + for (vref_idx = 0; vref_idx < MV_DDR4_VREF_MAX_RANGE; vref_idx++) { + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + if (((vref_byte_status[if_id][subphy_num][vref_idx]) & + (BYTE_HOMOGENEOUS_LOW | BYTE_SPLIT_OUT_MIX)) == + (BYTE_HOMOGENEOUS_LOW | BYTE_SPLIT_OUT_MIX)) { + if ((dq_vref_start_win[if_id][subphy_num][vref_idx] + + dq_vref_end_win[if_id][subphy_num][vref_idx]) / 2 <= 31) { + dq_vref_start_win[if_id][subphy_num][vref_idx] += 64; + dq_vref_end_win[if_id][subphy_num][vref_idx] += 64; + DEBUG_CALIBRATION + (DEBUG_LEVEL_TRACE, + ("%s vref_idx %d if %d subphy %d added 64 adlls to window\n", + __func__, valid_vref_ptr[if_id][subphy_num][vref_idx], + if_id, subphy_num)); + } + } + } + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("calculating center of mass for subphy %d, valid window size %d\n", + subphy_num, valid_win_size[if_id][subphy_num])); + if (valid_vref_cnt[if_id][subphy_num] > 0) { + /* calculate center of mass sampling point (t, v) for each subphy */ + status = mv_ddr4_center_of_mass_calc(dev_num, if_id, subphy_num, TX_DIR, + dq_vref_start_win[if_id][subphy_num], + dq_vref_end_win[if_id][subphy_num], + valid_vref_ptr[if_id][subphy_num], + valid_vref_cnt[if_id][subphy_num], + ¢er_vref[if_id][subphy_num], + ¢er_adll[if_id][subphy_num]); + if (status != MV_OK) + return status; + + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("center of mass results: vref %d, adll %d\n", + center_vref[if_id][subphy_num], center_adll[if_id][subphy_num])); + } else { + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("%s subphy %d no vref results to calculate the center of mass\n", + __func__, subphy_num)); + status = MV_ERROR; + return status; + } + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + vref_avg = 0; + vref_subphy_num = 0; + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + vref_avg += center_vref[if_id][subphy_num]; + dq_vref_vec[subphy_num] = center_vref[if_id][subphy_num]; + vref_subphy_num++; + } + + mv_ddr4_vref_tap_set(dev_num, if_id, ACCESS_TYPE_UNICAST, + vref_avg / vref_subphy_num, MV_DDR4_VREF_TAP_START); + mv_ddr4_vref_tap_set(dev_num, if_id, ACCESS_TYPE_UNICAST, + vref_avg / vref_subphy_num, MV_DDR4_VREF_TAP_END); + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, ("final vref average %d\n", vref_avg / vref_subphy_num)); + /* run centralization again with optimal vref to update global structures */ + mv_ddr4_centralization(dev_num, pbs_tap_factor, c_opt_per_bus, pbs_res_per_bus, valid_win_size, + TX_DIR, vref_avg / vref_subphy_num, duty_cycle); + } + + /* return dram from vref DRAM from vref training mode */ + mv_ddr4_vref_training_mode_ctrl(dev_num, 0, ACCESS_TYPE_MULTICAST, 0); + + /* dqs tx reposition calculation */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + pad_num = dq_map_table[dq_idx + + subphy_num * BUS_WIDTH_IN_BITS + + if_id * BUS_WIDTH_IN_BITS * subphy_max]; + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + 0x10 + pad_num + effective_cs * 0x10, + pbs_res_per_bus[if_id][subphy_num][dq_idx]); + if (status != MV_OK) + return status; + } + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + CTX_PHY_REG(effective_cs), + center_adll[if_id][subphy_num] % 64); + if (status != MV_OK) + return status; + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + /* report pass for all active interfaces; multi-interface support - tbd */ + flow_result[if_id] = TEST_SUCCESS; + } + + return MV_OK; +} + +/* centralization flow */ +static int mv_ddr4_centralization(u8 dev_num, u16 (*lambda)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS], u8 (*copt)[MAX_BUS_NUM], + u8 (*pbs_result)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS], u8 (*vw_size)[MAX_BUS_NUM], + u8 mode, u16 param0, u8 param1) +{ +/* FIXME: remove the dependency in 64bit */ +#define MV_DDR_NUM_OF_CENTRAL_PATTERNS (PATTERN_KILLER_DQ7 - PATTERN_KILLER_DQ0 + 1) + static u8 subphy_end_win[MAX_DIR_TYPES][MAX_INTERFACE_NUM][MAX_BUS_NUM]; + static u8 subphy_start_win[MAX_DIR_TYPES][MAX_INTERFACE_NUM][MAX_BUS_NUM]; + static u8 final_start_win[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + static u8 final_end_win[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM]; + u32 if_id, subphy_num, pattern_id, pattern_loop_idx, bit_num; + u8 curr_start_win[BUS_WIDTH_IN_BITS]; + u8 curr_end_win[BUS_WIDTH_IN_BITS]; + static u8 start_win_db[MV_DDR_NUM_OF_CENTRAL_PATTERNS][MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + static u8 end_win_db[MV_DDR_NUM_OF_CENTRAL_PATTERNS][MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + u8 curr_win[BUS_WIDTH_IN_BITS]; + u8 opt_win, waste_win, start_win_skew, end_win_skew; + u8 final_subphy_win[MAX_INTERFACE_NUM][BUS_WIDTH_IN_BITS]; + enum hws_training_result result_type = RESULT_PER_BIT; + enum hws_dir direction; + enum hws_search_dir search_dir; + u32 *result[HWS_SEARCH_DIR_LIMIT]; + u32 max_win_size; + u8 curr_end_win_min, curr_start_win_max; + u32 cs_ena_reg_val[MAX_INTERFACE_NUM]; + u8 current_byte_status; + int status; + struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get(); + u8 subphy_max = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + /* save current cs enable reg val */ + status = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, DUAL_DUNIT_CFG_REG, + cs_ena_reg_val, MASK_ALL_BITS); + if (status != MV_OK) + return status; + + /* enable single cs */ + status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, if_id, DUAL_DUNIT_CFG_REG, + (0x1 << 3), (0x1 << 3)); + if (status != MV_OK) + return status; + } + + if (mode == TX_DIR) { + max_win_size = MAX_WINDOW_SIZE_TX; + direction = OPER_WRITE; + } else { + max_win_size = MAX_WINDOW_SIZE_RX; + direction = OPER_READ; + } + + /* database initialization */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + patterns_byte_status[if_id][subphy_num] = BYTE_NOT_DEFINED; + subphy_end_win[mode][if_id][subphy_num] = (max_win_size - 1); + subphy_start_win[mode][if_id][subphy_num] = 0; + vw_size[if_id][subphy_num] = (max_win_size - 1); + for (bit_num = 0; bit_num < BUS_WIDTH_IN_BITS; bit_num++) { + final_start_win[if_id][subphy_num][bit_num] = 0; + final_end_win[if_id][subphy_num][bit_num] = (max_win_size - 1); + if (mode == TX_DIR) + final_end_win[if_id][subphy_num][bit_num] = (2 * max_win_size - 1); + } + if (mode == TX_DIR) { + subphy_end_win[mode][if_id][subphy_num] = (2 * max_win_size - 1); + vw_size[if_id][subphy_num] = (2 * max_win_size - 1); + } + } + } + + /* main flow */ + /* FIXME: hard-coded "22" below for PATTERN_KILLER_DQ7_64 enum hws_pattern */ + for (pattern_id = PATTERN_KILLER_DQ0, pattern_loop_idx = 0; + pattern_id <= (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask) ? 22 : PATTERN_KILLER_DQ7); + pattern_id++, pattern_loop_idx++) { + ddr3_tip_ip_training_wrapper(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, HWS_CONTROL_ELEMENT_ADLL, + PARAM_NOT_CARE, direction, tm->if_act_mask, + 0x0, max_win_size - 1, max_win_size - 1, pattern_id, + EDGE_FPF, CS_SINGLE, PARAM_NOT_CARE, training_result); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + /* + * in case the previous patterns found the current subphy as BYTE_NOT_DEFINED, + * continue to next subphy + */ + if ((patterns_byte_status[if_id][subphy_num] == BYTE_NOT_DEFINED) && + (pattern_id != PATTERN_KILLER_DQ0)) + continue; + /* + * in case the result of the current subphy is BYTE_NOT_DEFINED mark the + * pattern byte status as BYTE_NOT_DEFINED + */ + current_byte_status = mv_ddr_tip_sub_phy_byte_status_get(if_id, subphy_num); + if (current_byte_status == BYTE_NOT_DEFINED) { + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_INFO, + ("%s:%s: failed to lock subphy, pat %d if %d subphy %d\n", + __func__, str_dir[mode], pattern_id, if_id, subphy_num)); + patterns_byte_status[if_id][subphy_num] = BYTE_NOT_DEFINED; + /* update the valid window size which is return value from this function */ + vw_size[if_id][subphy_num] = 0; + /* continue to next subphy */ + continue; + } + + /* set the status of this byte */ + patterns_byte_status[if_id][subphy_num] |= current_byte_status; + for (search_dir = HWS_LOW2HIGH; search_dir <= HWS_HIGH2LOW; search_dir++) { + status = ddr3_tip_read_training_result(dev_num, if_id, ACCESS_TYPE_UNICAST, + subphy_num, ALL_BITS_PER_PUP, + search_dir, direction, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, &result[search_dir], + 1, 0, 0); + if (status != MV_OK) + return status; + + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_INFO, + ("param0 %d param1 %d pat %d if %d subphy %d " + "regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + param0, param1, pattern_id, if_id, subphy_num, + result[search_dir][0], result[search_dir][1], + result[search_dir][2], result[search_dir][3], + result[search_dir][4], result[search_dir][5], + result[search_dir][6], result[search_dir][7])); + } + + for (bit_num = 0; bit_num < BUS_WIDTH_IN_BITS; bit_num++) { + /* read result success */ + DEBUG_DDR4_CENTRALIZATION( + DEBUG_LEVEL_INFO, + ("%s %s subphy locked, pat %d if %d subphy %d\n", + __func__, str_dir[mode], pattern_id, + if_id, subphy_num)); + start_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] = + GET_TAP_RESULT(result[HWS_LOW2HIGH][bit_num], EDGE_1); + end_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] = + GET_TAP_RESULT(result[HWS_HIGH2LOW][bit_num], EDGE_1); + } + } /* subphy */ + } /* interface */ + } /* pattern */ + + /* + * check if the current patterns subphys in all interfaces has mixed and low byte states + * in that case add 64 adlls to the low byte + */ + for (pattern_id = PATTERN_KILLER_DQ0, pattern_loop_idx = 0; + pattern_id <= (MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask) ? 22 : PATTERN_KILLER_DQ7); + pattern_id++, pattern_loop_idx++) { + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + if (patterns_byte_status[if_id][subphy_num] == BYTE_NOT_DEFINED) + continue; + opt_win = 2 * max_win_size; /* initialize opt_win */ + /* in case this byte in the pattern is homogeneous low add 64 adlls to the byte */ + if (((patterns_byte_status[if_id][subphy_num]) & + (BYTE_HOMOGENEOUS_LOW | BYTE_SPLIT_OUT_MIX)) == + (BYTE_HOMOGENEOUS_LOW | BYTE_SPLIT_OUT_MIX)) { + for (bit_num = 0; bit_num < BUS_WIDTH_IN_BITS; bit_num++) { + if (start_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] <= 31 && + end_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] <= 31) { + start_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] += + 64; + end_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] += 64; + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_INFO, + ("%s %s pattern %d if %d subphy %d bit %d added 64 " + "adll\n", + __func__, str_dir[mode], pattern_id, if_id, + subphy_num, bit_num)); + } + } + } + + /* calculations for the current pattern per subphy */ + for (bit_num = 0; bit_num < BUS_WIDTH_IN_BITS; bit_num++) { + curr_win[bit_num] = end_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] - + start_win_db[pattern_loop_idx][if_id][subphy_num][bit_num] + 1; + curr_start_win[bit_num] = + start_win_db[pattern_loop_idx][if_id][subphy_num][bit_num]; + curr_end_win[bit_num] = + end_win_db[pattern_loop_idx][if_id][subphy_num][bit_num]; + } + + opt_win = GET_MIN(opt_win, ddr3_tip_get_buf_min(curr_win)); + vw_size[if_id][subphy_num] = + GET_MIN(vw_size[if_id][subphy_num], ddr3_tip_get_buf_min(curr_win)); + + /* final subphy window length */ + final_subphy_win[if_id][subphy_num] = ddr3_tip_get_buf_min(curr_end_win) - + ddr3_tip_get_buf_max(curr_start_win) + 1; + waste_win = opt_win - final_subphy_win[if_id][subphy_num]; + start_win_skew = ddr3_tip_get_buf_max(curr_start_win) - + ddr3_tip_get_buf_min(curr_start_win); + end_win_skew = ddr3_tip_get_buf_max(curr_end_win) - + ddr3_tip_get_buf_min(curr_end_win); + + /* min/max updated with pattern change */ + curr_end_win_min = ddr3_tip_get_buf_min(curr_end_win); + curr_start_win_max = ddr3_tip_get_buf_max(curr_start_win); + subphy_end_win[mode][if_id][subphy_num] = + GET_MIN(subphy_end_win[mode][if_id][subphy_num], curr_end_win_min); + subphy_start_win[mode][if_id][subphy_num] = + GET_MAX(subphy_start_win[mode][if_id][subphy_num], curr_start_win_max); + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_TRACE, + ("%s, %s pat %d if %d subphy %d opt_win %d ", + __func__, str_dir[mode], pattern_id, if_id, subphy_num, opt_win)); + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_TRACE, + ("final_subphy_win %d waste_win %d " + "start_win_skew %d end_win_skew %d ", + final_subphy_win[if_id][subphy_num], + waste_win, start_win_skew, end_win_skew)); + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("curr_start_win_max %d curr_end_win_min %d " + "subphy_start_win %d subphy_end_win %d\n", + curr_start_win_max, curr_end_win_min, + subphy_start_win[mode][if_id][subphy_num], + subphy_end_win[mode][if_id][subphy_num])); + + /* valid window */ + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("valid window, pat %d if %d subphy %d\n", + pattern_id, if_id, subphy_num)); + for (bit_num = 0; bit_num < BUS_WIDTH_IN_BITS; bit_num++) { + final_start_win[if_id][subphy_num][bit_num] = + GET_MAX(final_start_win[if_id][subphy_num][bit_num], + curr_start_win[bit_num]); + final_end_win[if_id][subphy_num][bit_num] = + GET_MIN(final_end_win[if_id][subphy_num][bit_num], + curr_end_win[bit_num]); + } /* bit */ + } /* subphy */ + } /* if_id */ + } /* pattern */ + + /* calculate valid window for each subphy */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + if (patterns_byte_status[if_id][subphy_num] != BYTE_NOT_DEFINED) { + /* + * in case of bytes status which were found as mixed and low + * change the their status to be mixed only, due to the fact + * that we have already dealt with this bytes by adding 64 adlls + * to the low bytes + */ + if (patterns_byte_status[if_id][subphy_num] & + (BYTE_HOMOGENEOUS_LOW | BYTE_SPLIT_OUT_MIX)) + patterns_byte_status[if_id][subphy_num] = BYTE_SPLIT_OUT_MIX; + if (rx_vw_pos[if_id][subphy_num] == 0) /* rx_vw_pos is initialized during tap tune */ + pbs_max = 31 - 0xa; + else + pbs_max = 31; + + /* continue if locked */ + /*if (centralization_state[if_id][subphy_num] == 0) {*/ + status = mv_ddr4_copt_get(mode, lambda[if_id][subphy_num], + final_start_win[if_id][subphy_num], + final_end_win[if_id][subphy_num], + pbs_result[if_id][subphy_num], + &copt[if_id][subphy_num]); + + /* + * after copt the adll is moved to smaller value due to pbs compensation + * so the byte status might change, here we change the byte status to be + * homogeneous low in case the center of the ui after copt is moved below + * 31 adlls + */ + if(copt[if_id][subphy_num] <= 31) + patterns_byte_status[if_id][subphy_num] = BYTE_HOMOGENEOUS_LOW; + + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_INFO, + ("%s %s if %d subphy %d copt %d\n", + __func__, str_dir[mode], if_id, subphy_num, copt[if_id][subphy_num])); + + if (status != MV_OK) { + /* + * TODO: print out error message(s) only when all points fail + * as temporary solution, replaced ERROR to TRACE debug level + */ + DEBUG_DDR4_CENTRALIZATION + (DEBUG_LEVEL_TRACE, + ("%s %s copt calculation failed, " + "no valid window for subphy %d\n", + __func__, str_dir[mode], subphy_num)); + /* set the byte to 0 (fail) and clean the status (continue with algorithm) */ + vw_size[if_id][subphy_num] = 0; + status = MV_OK; + + if (debug_mode == 0) { + /* + * TODO: print out error message(s) only when all points fail + * as temporary solution, commented out debug level set to TRACE + */ + /* + * ddr3_hws_set_log_level(DEBUG_BLOCK_CALIBRATION, DEBUG_LEVEL_TRACE); + */ + /* open relevant log and run function again for debug */ + mv_ddr4_copt_get(mode, lambda[if_id][subphy_num], + final_start_win[if_id][subphy_num], + final_end_win[if_id][subphy_num], + pbs_result[if_id][subphy_num], + &copt[if_id][subphy_num]); + /* + * ddr3_hws_set_log_level(DEBUG_BLOCK_CALIBRATION, DEBUG_LEVEL_ERROR); + */ + } /* debug mode */ + } /* status */ + } /* byte not defined */ + } /* subphy */ + } /* if_id */ + + /* restore cs enable value*/ + for (if_id = 0; if_id < MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + status = ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, if_id, DUAL_DUNIT_CFG_REG, + cs_ena_reg_val[if_id], MASK_ALL_BITS); + if (status != MV_OK) + return status; + } + + return status; +} + +/* + * mv_ddr4_copt_get function + * inputs: + * dir - direction; 0 is for rx, 1 for tx + * lambda - a pointer to adll to pbs ration multiplied by PBS_VAL_FACTOR + * vw_l - a pointer to valid window low limit in adll taps + * vw_h - a pointer to valid window high limit in adll taps + * outputs: + * pbs_result - a pointer to pbs new delay value; the function's output + * copt - optimal center of subphy in adll taps + * The function assumes initial pbs tap value is zero. Otherwise, it requires logic + * getting pbs value per dq and setting pbs_taps_per_dq array. + * It provides with a solution for a single subphy (8 bits). + * The calling function is responsible for any additional pbs taps for dqs + */ +static int mv_ddr4_copt_get(u8 dir, u16 *lambda, u8 *vw_l, u8 *vw_h, u8 *pbs_result, u8 *copt) +{ + u8 center_per_dq[8]; + u8 center_zone_low[8] = {0}; + u8 center_zone_high[8] = {0}; + u8 ext_center_zone_low[8] = {0}; + u8 ext_center_zone_high[8] = {0}; + u8 pbs_taps_per_dq[8] = {0}; + u8 vw_per_dq[8]; + u8 vw_zone_low[8] = {0}; + u8 vw_zone_high[8] = {0}; + u8 margin_vw[8] = {0}; + u8 copt_val; + u8 dq_idx; + u8 center_zone_max_low = 0; + u8 center_zone_min_high = 128; + u8 vw_zone_max_low = 0; + u8 vw_zone_min_high = 128; + u8 min_vw = 63; /* minimum valid window between all bits */ + u8 center_low_el; + u8 center_high_el; + + /* lambda calculated as D * PBS_VALUE_FACTOR / d */ + //printf("Copt::Debug::\t"); + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + center_per_dq[dq_idx] = (vw_h[dq_idx] + vw_l[dq_idx]) / 2; + vw_per_dq[dq_idx] = 1 + (vw_h[dq_idx] - vw_l[dq_idx]); + if (min_vw > vw_per_dq[dq_idx]) + min_vw = vw_per_dq[dq_idx]; + } + + /* calculate center zone */ + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + center_low_el = center_low_element_get(dir, pbs_taps_per_dq[dq_idx], lambda[dq_idx], pbs_max); + if (center_per_dq[dq_idx] > center_low_el) + center_zone_low[dq_idx] = center_per_dq[dq_idx] - center_low_el; + center_high_el = center_high_element_get(dir, pbs_taps_per_dq[dq_idx], lambda[dq_idx], pbs_max); + center_zone_high[dq_idx] = center_per_dq[dq_idx] + center_high_el; + if (center_zone_max_low < center_zone_low[dq_idx]) + center_zone_max_low = center_zone_low[dq_idx]; + if (center_zone_min_high > center_zone_high[dq_idx]) + center_zone_min_high = center_zone_high[dq_idx]; + DEBUG_CALIBRATION(DEBUG_LEVEL_TRACE, + ("center: low %d, high %d, max_low %d, min_high %d\n", + center_zone_low[dq_idx], center_zone_high[dq_idx], + center_zone_max_low, center_zone_min_high)); + } + + if (center_zone_min_high >= center_zone_max_low) { /* center zone visib */ + /* set copt_val to high zone for rx */ + copt_val = (dir == RX_DIR) ? center_zone_max_low : center_zone_min_high; + *copt = copt_val; + + /* calculate additional pbs taps */ + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + if (dir == RX_DIR) + pbs_result[dq_idx] = (copt_val - center_per_dq[dq_idx]) * + PBS_VAL_FACTOR / lambda[dq_idx]; + else + pbs_result[dq_idx] = (center_per_dq[dq_idx] - copt_val) * + PBS_VAL_FACTOR / lambda[dq_idx]; + } + return MV_OK; + } else { /* not center zone visib */ + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + if ((center_zone_low[dq_idx] + 1) > (vw_per_dq[dq_idx] / 2 + vw_per_dq[dq_idx] % 2)) { + vw_zone_low[dq_idx] = (center_zone_low[dq_idx] + 1) - + (vw_per_dq[dq_idx] / 2 + vw_per_dq[dq_idx] % 2); + } else { + vw_zone_low[dq_idx] = 0; + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("dq_idx %d, center zone low %d, vw_l %d, vw_l %d\n", + dq_idx, center_zone_low[dq_idx], vw_l[dq_idx], vw_h[dq_idx])); + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("vw_l[%d], vw_lh[%d], lambda[%d]\n", + vw_l[dq_idx], vw_h[dq_idx], lambda[dq_idx])); + } + + vw_zone_high[dq_idx] = center_zone_high[dq_idx] + vw_per_dq[dq_idx] / 2; + + if (vw_zone_max_low < vw_zone_low[dq_idx]) + vw_zone_max_low = vw_zone_low[dq_idx]; + + if (vw_zone_min_high > vw_zone_high[dq_idx]) + vw_zone_min_high = vw_zone_high[dq_idx]; + + DEBUG_CALIBRATION(DEBUG_LEVEL_TRACE, + ("valid_window: low %d, high %d, max_low %d, min_high %d\n", + vw_zone_low[dq_idx], vw_zone_high[dq_idx], + vw_zone_max_low, vw_zone_min_high)); + } + + /* try to extend center zone */ + if (vw_zone_min_high >= vw_zone_max_low) { /* vw zone visib */ + center_zone_max_low = 0; + center_zone_min_high = 128; + + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + margin_vw[dq_idx] = vw_per_dq[dq_idx] - min_vw; + + if (center_zone_low[dq_idx] > margin_vw[dq_idx]) + ext_center_zone_low[dq_idx] = center_zone_low[dq_idx] - margin_vw[dq_idx]; + else + ext_center_zone_low[dq_idx] = 0; + + ext_center_zone_high[dq_idx] = center_zone_high[dq_idx] + margin_vw[dq_idx]; + + if (center_zone_max_low < ext_center_zone_low[dq_idx]) + center_zone_max_low = ext_center_zone_low[dq_idx]; + + if (center_zone_min_high > ext_center_zone_high[dq_idx]) + center_zone_min_high = ext_center_zone_high[dq_idx]; + + DEBUG_CALIBRATION(DEBUG_LEVEL_TRACE, + ("ext_center: low %d, high %d, max_low %d, min_high %d\n", + ext_center_zone_low[dq_idx], ext_center_zone_high[dq_idx], + center_zone_max_low, center_zone_min_high)); + } + + if (center_zone_min_high >= center_zone_max_low) { /* center zone visib */ + /* get optimal center position */ + copt_val = (dir == RX_DIR) ? center_zone_max_low : center_zone_min_high; + *copt = copt_val; + + /* calculate additional pbs taps */ + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + if (dir == 0) { + if (copt_val > center_per_dq[dq_idx]) + pbs_result[dq_idx] = (copt_val - center_per_dq[dq_idx]) * + PBS_VAL_FACTOR / lambda[dq_idx]; + else + pbs_result[dq_idx] = 0; + } else { + if (center_per_dq[dq_idx] > copt_val) + pbs_result[dq_idx] = (center_per_dq[dq_idx] - copt_val) * + PBS_VAL_FACTOR / lambda[dq_idx]; + else + pbs_result[dq_idx] = 0; + } + + if (pbs_result[dq_idx] > pbs_max) + pbs_result[dq_idx] = pbs_max; + } + + return MV_OK; + } else { /* not center zone visib */ + /* + * TODO: print out error message(s) only when all points fail + * as temporary solution, replaced ERROR to TRACE debug level + */ + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("lambda: %d, %d, %d, %d, %d, %d, %d, %d\n", + lambda[0], lambda[1], lambda[2], lambda[3], + lambda[4], lambda[5], lambda[6], lambda[7])); + + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("vw_h: %d, %d, %d, %d, %d, %d, %d, %d\n", + vw_h[0], vw_h[1], vw_h[2], vw_h[3], + vw_h[4], vw_h[5], vw_h[6], vw_h[7])); + + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("vw_l: %d, %d, %d, %d, %d, %d, %d, %d\n", + vw_l[0], vw_l[1], vw_l[2], vw_l[3], + vw_l[4], vw_l[5], vw_l[6], vw_l[7])); + + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("center: low %d, high %d, " + "max_low %d, min_high %d\n", + center_zone_low[dq_idx], center_zone_high[dq_idx], + center_zone_max_low, center_zone_min_high)); + + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("valid_window: low %d, high %d, " + "max_low %d, min_high %d\n", + vw_zone_low[dq_idx], vw_zone_high[dq_idx], + vw_zone_max_low, vw_zone_min_high)); + + DEBUG_DDR4_CENTRALIZATION(DEBUG_LEVEL_TRACE, + ("ext_center: low %d, high %d, " + "max_low %d, min_high %d\n", + ext_center_zone_low[dq_idx], + ext_center_zone_high[dq_idx], + center_zone_max_low, center_zone_min_high)); + } + + return MV_FAIL; + } + } else { /* not vw zone visib; failed to find a single sample point */ + return MV_FAIL; + } + } + + return MV_OK; +} + +/* + * mv_ddr4_dqs_reposition function gets copt to align to and returns pbs value per bit + * parameters: + * dir - direction; 0 is for rx, 1 for tx + * lambda - a pointer to adll to pbs ration multiplied by PBS_VAL_FACTOR + * pbs_result - a pointer to pbs new delay value; the function's output + * delta - signed; possilbe values: +0xa, 0x0, -0xa; for rx can be only negative + * copt - optimal center of subphy in adll taps + * dqs_pbs - optimal pbs + * The function assumes initial pbs tap value is zero. Otherwise, it requires logic + * getting pbs value per dq and setting pbs_taps_per_dq array. + * It provides with a solution for a single subphy (8 bits). + * The calling function is responsible for any additional pbs taps for dqs + */ +static int mv_ddr4_dqs_reposition(u8 dir, u16 *lambda, u8 *pbs_result, char delta, u8 *copt, u8 *dqs_pbs) +{ + u8 dq_idx; + u32 pbs_max_val = 0; + u32 lambda_avg = 0; + + /* lambda calculated as D * X / d */ + for (dq_idx = 0; dq_idx < 8; dq_idx++) { + if (pbs_max_val < pbs_result[dq_idx]) + pbs_max_val = pbs_result[dq_idx]; + lambda_avg += lambda[dq_idx]; + } + + if (delta >= 0) + *dqs_pbs = (pbs_max_val + delta) / 2; + else /* dqs already 0xa */ + *dqs_pbs = pbs_max_val / 2; + + lambda_avg /= 8; + + /* change in dqs pbs value requires change in final copt position from mass center solution */ + if (dir == TX_DIR) { + /* for tx, additional pbs on dqs in opposite direction of adll */ + *copt = *copt + ((*dqs_pbs) * lambda_avg) / PBS_VAL_FACTOR; + } else { + /* for rx, additional pbs on dqs in same direction of adll */ + if (delta < 0) + *copt = *copt - ((*dqs_pbs + delta) * lambda_avg) / PBS_VAL_FACTOR; + else + *copt = *copt - (*dqs_pbs * lambda_avg) / PBS_VAL_FACTOR; + } + + return MV_OK; +} + +/* + * mv_ddr4_center_of_mass_calc function + * parameters: + * vw_l - a pointer to valid window low limit in adll taps + * vw_h - a pointer to valid window high limit in adll taps + * vw_v - a pointer to vref value matching vw_l/h arrays + * vw_num - number of valid windows (lenght vw_v vector) + * v_opt - optimal voltage value in vref taps + * t_opt - optimal adll value in adll taps + * This function solves 2D centroid equation (e.g., adll and vref axes) + * The function doesn't differentiate between byte and bit eyes + */ +static int mv_ddr4_center_of_mass_calc(u8 dev_num, u8 if_id, u8 subphy_num, u8 mode, u8 *vw_l, + u8 *vw_h, u8 *vw_v, u8 vw_num, u8 *v_opt, u8 *t_opt) +{ + u8 idx; + u8 edge_t[128], edge_v[128]; + u8 min_edge_t = 127, min_edge_v = 127; + int polygon_area = 0; + int t_opt_temp = 0, v_opt_temp = 0; + int vw_avg = 0, v_avg = 0; + int s0 = 0, s1 = 0, s2 = 0, slope = 1, r_sq = 0; + u32 d_min = 10000, reg_val = 0; + int status; + + /* + * reorder all polygon points counterclockwise + * get min value of each axis to shift to smaller calc value + */ + for (idx = 0; idx < vw_num; idx++) { + edge_t[idx] = vw_l[idx]; + edge_v[idx] = vw_v[idx]; + if (min_edge_v > vw_v[idx]) + min_edge_v = vw_v[idx]; + if (min_edge_t > vw_l[idx]) + min_edge_t = vw_l[idx]; + edge_t[vw_num * 2 - 1 - idx] = vw_h[idx]; + edge_v[vw_num * 2 - 1 - idx] = vw_v[idx]; + vw_avg += vw_h[idx] - vw_l[idx]; + v_avg += vw_v[idx]; + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("%s: if %d, byte %d, direction %d, vw_v %d, vw_l %d, vw_h %d\n", + __func__, if_id, subphy_num, mode, vw_v[idx], vw_l[idx], vw_h[idx])); + } + + vw_avg *= 1000 / vw_num; + v_avg /= vw_num; + for (idx = 0; idx < vw_num; idx++) { + s0 += (1000 * (vw_h[idx] - vw_l[idx]) - vw_avg) * (vw_v[idx] - v_avg); + s1 += (vw_v[idx] - v_avg) * (vw_v[idx] - v_avg); + s2 += (1000 * (vw_h[idx] - vw_l[idx]) - vw_avg) * (1000 * (vw_h[idx] - vw_l[idx]) - vw_avg); + } + r_sq = s0 * (s0 / s1); + r_sq /= (s2 / 1000); + slope = s0 / s1; + + /* idx n is equal to idx 0 */ + edge_t[vw_num * 2] = vw_l[0]; + edge_v[vw_num * 2] = vw_v[0]; + + /* calculate polygon area, a (may be negative) */ + for (idx = 0; idx < vw_num * 2; idx++) + polygon_area = polygon_area + + ((edge_t[idx] - min_edge_t)*(edge_v[idx + 1] - min_edge_v) - + (edge_t[idx + 1] - min_edge_t)*(edge_v[idx] - min_edge_v)); + + /* calculate optimal point */ + for (idx = 0; idx < vw_num * 2; idx++) { + t_opt_temp = t_opt_temp + + (edge_t[idx] + edge_t[idx + 1] - 2 * min_edge_t) * + ((edge_t[idx] - min_edge_t)*(edge_v[idx + 1] - min_edge_v) - + (edge_t[idx + 1] - min_edge_t)*(edge_v[idx] - min_edge_v)); + v_opt_temp = v_opt_temp + + (edge_v[idx] + edge_v[idx + 1] - 2 * min_edge_v) * + ((edge_t[idx] - min_edge_t)*(edge_v[idx + 1] - min_edge_v) - + (edge_t[idx + 1] - min_edge_t)*(edge_v[idx] - min_edge_v)); + } + + *t_opt = t_opt_temp / (3 * polygon_area); + *v_opt = v_opt_temp / (3 * polygon_area); + + /* re-shift */ + *t_opt += min_edge_t; + *v_opt += min_edge_v; + + /* calculate d_min */ + for (idx = 0; idx < 2 * vw_num; idx++) { + s0 = (*t_opt - edge_t[idx]) * (*t_opt - edge_t[idx]) + + (*v_opt - edge_v[idx]) * (*v_opt - edge_v[idx]); + d_min = (d_min > s0) ? s0 : d_min; + } + DEBUG_CALIBRATION(DEBUG_LEVEL_TRACE, + ("%s: r_sq %d, slope %d, area = %d, , d_min = %d\n", + __func__, r_sq, slope, polygon_area, d_min)); + + /* insert vw eye to register database for validation */ + if (d_min < 0) + d_min = -d_min; + if (polygon_area < 0) + polygon_area = -polygon_area; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, ACCESS_TYPE_UNICAST, subphy_num, + DDR_PHY_DATA, RESULT_PHY_REG + effective_cs + 4 * (1 - mode), + polygon_area); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, + dmin_phy_reg_table[effective_cs * 5 + subphy_num][0], DDR_PHY_CONTROL, + dmin_phy_reg_table[effective_cs * 5 + subphy_num][1], ®_val); + if (status != MV_OK) + return status; + + reg_val &= 0xff << (8 * mode); /* rx clean bits 0..8, tx bits 9..16 */ + reg_val |= d_min / 2 << (8 * (1 - mode)); /* rX write bits 0..8, tx bits 9..16 */ + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, ACCESS_TYPE_UNICAST, + dmin_phy_reg_table[effective_cs * 5 + subphy_num][0], DDR_PHY_CONTROL, + dmin_phy_reg_table[effective_cs * 5 + subphy_num][1], reg_val); + if (status != MV_OK) + return status; + + if (polygon_area < 400) { + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("%s: if %d, subphy %d: poligon area too small %d (dmin %d)\n", + __func__, if_id, subphy_num, polygon_area, d_min)); + if (debug_mode == 0) + return MV_FAIL; + } + + return MV_OK; +} + +/* tap tuning flow */ +enum { + DQS_TO_DQ_LONG, + DQS_TO_DQ_SHORT +}; +enum { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT +}; +#define ONE_MHZ 1000000 +#define MAX_SKEW_DLY 200 /* in ps */ +#define NOMINAL_PBS_DLY 9 /* in ps */ +#define MIN_WL_TO_CTX_ADLL_DIFF 2 /* in taps */ +#define DQS_SHIFT_INIT_VAL 30 +#define MAX_PBS_NUM 31 +#define ADLL_TAPS_PER_PHASE 32 +#define ADLL_TAPS_PER_PERIOD (ADLL_TAPS_PER_PHASE * 2) +#define ADLL_TX_RES_REG_MASK 0xff +#define VW_DESKEW_BIAS 0xa +static int mv_ddr4_tap_tuning(u8 dev, u16 (*pbs_tap_factor)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS], u8 mode) +{ + enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM]; + u32 iface, subphy, bit, pattern; + u32 limit_div; + u8 curr_start_win, curr_end_win; + u8 upd_curr_start_win, upd_curr_end_win; + u8 start_win_diff, end_win_diff; + u32 max_win_size, a, b; + u32 cs_ena_reg_val[MAX_INTERFACE_NUM]; + u32 reg_addr; + enum hws_search_dir search_dir; + enum hws_dir dir; + u32 *result[MAX_BUS_NUM][HWS_SEARCH_DIR_LIMIT]; + u32 result1[MAX_BUS_NUM][HWS_SEARCH_DIR_LIMIT][BUS_WIDTH_IN_BITS]; + u8 subphy_max = ddr3_tip_dev_attr_get(dev, MV_ATTR_OCTET_PER_INTERFACE); + struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get(); + enum hws_training_result result_type = RESULT_PER_BIT; + int status = MV_OK; + int i; + u32 reg_val; + u32 freq = mv_ddr_freq_get(tm->interface_params->memory_freq); + /* calc adll tap in ps based on frequency */ + int adll_tap = (ONE_MHZ / freq) / ADLL_TAPS_PER_PERIOD; + int dq_to_dqs_delta[MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; /* skew b/w dq and dqs */ + u32 wl_adll[MAX_BUS_NUM]; /* wl solution adll value */ + int is_dq_dqs_short[MAX_BUS_NUM] = {0}; /* tx byte's state */ + u32 new_pbs_per_byte[MAX_BUS_NUM]; /* dq pads' pbs value correction */ + /* threshold to decide subphy needs dqs pbs delay */ + int dq_to_dqs_min_delta_threshold = MIN_WL_TO_CTX_ADLL_DIFF + MAX_SKEW_DLY / adll_tap; + /* search init condition */ + int dq_to_dqs_min_delta = dq_to_dqs_min_delta_threshold * 2; + u32 pbs_tap_factor0 = PBS_VAL_FACTOR * NOMINAL_PBS_DLY / adll_tap; /* init lambda */ + /* adapt pbs to frequency */ + u32 new_pbs = (1810000 - (345 * freq)) / 100000; + int stage_num, loop; + int wl_tap, new_wl_tap; + int pbs_tap_factor_avg; + int dqs_shift[MAX_BUS_NUM]; /* dqs' pbs delay */ + static u16 tmp_pbs_tap_factor[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, ("Starting ddr4 tap tuning training stage\n")); + + for (i = 0; i < MAX_BUS_NUM; i++) + dqs_shift[i] = DQS_SHIFT_INIT_VAL; + + if (mode == TX_DIR) { + max_win_size = MAX_WINDOW_SIZE_TX; + dir = OPER_WRITE; + } else { + max_win_size = MAX_WINDOW_SIZE_RX; + dir = OPER_READ; + } + + /* init all pbs registers */ + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + if (mode == RX_DIR) + reg_addr = PBS_RX_BCAST_PHY_REG(effective_cs); + else + reg_addr = PBS_TX_BCAST_PHY_REG(effective_cs); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, DDR_PHY_DATA, reg_addr, 0); + + if (mode == RX_DIR) + reg_addr = PBS_RX_PHY_REG(effective_cs, DQSP_PAD); + else + reg_addr = PBS_TX_PHY_REG(effective_cs, DQSP_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, DDR_PHY_DATA, reg_addr, 0); + if (mode == RX_DIR) + reg_addr = PBS_RX_PHY_REG(effective_cs, DQSN_PAD); + else + reg_addr = PBS_TX_PHY_REG(effective_cs, DQSN_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, DDR_PHY_DATA, reg_addr, 0); + } + + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + /* save current cs enable reg val */ + ddr3_tip_if_read(dev, ACCESS_TYPE_UNICAST, iface, DUAL_DUNIT_CFG_REG, + cs_ena_reg_val, MASK_ALL_BITS); + + /* enable single cs */ + ddr3_tip_if_write(dev, ACCESS_TYPE_UNICAST, iface, DUAL_DUNIT_CFG_REG, + (SINGLE_CS_ENA << SINGLE_CS_PIN_OFFS), + (SINGLE_CS_PIN_MASK << SINGLE_CS_PIN_OFFS)); + } + + /* FIXME: fix this hard-coded parameters due to compilation issue with patterns definitions */ + pattern = MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask) ? 73 : 23; + stage_num = (mode == RX_DIR) ? 1 : 2; + /* find window; run training */ + for (loop = 0; loop < stage_num; loop++) { + ddr3_tip_ip_training_wrapper(dev, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, HWS_CONTROL_ELEMENT_ADLL, PARAM_NOT_CARE, + dir, tm->if_act_mask, 0x0, max_win_size - 1, max_win_size - 1, + pattern, EDGE_FPF, CS_SINGLE, PARAM_NOT_CARE, training_result); + + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + rx_vw_pos[iface][subphy] = ALIGN_CENTER; + new_pbs_per_byte[subphy] = new_pbs; /* rx init */ + if ((mode == TX_DIR) && (loop == 0)) { + /* read nominal wl */ + ddr3_tip_bus_read(dev, iface, ACCESS_TYPE_UNICAST, subphy, + DDR_PHY_DATA, WL_PHY_REG(effective_cs), + ®_val); + wl_adll[subphy] = reg_val; + } + + for (search_dir = HWS_LOW2HIGH; search_dir <= HWS_HIGH2LOW; search_dir++) { + ddr3_tip_read_training_result(dev, iface, ACCESS_TYPE_UNICAST, subphy, + ALL_BITS_PER_PUP, search_dir, dir, + result_type, TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, &(result[subphy][search_dir]), + 1, 0, 0); + + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d mode %d result: " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + effective_cs, iface, subphy, mode, + result[subphy][search_dir][0], + result[subphy][search_dir][1], + result[subphy][search_dir][2], + result[subphy][search_dir][3], + result[subphy][search_dir][4], + result[subphy][search_dir][5], + result[subphy][search_dir][6], + result[subphy][search_dir][7])); + } + + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + a = result[subphy][HWS_LOW2HIGH][bit]; + b = result[subphy][HWS_HIGH2LOW][bit]; + result1[subphy][HWS_LOW2HIGH][bit] = a; + result1[subphy][HWS_HIGH2LOW][bit] = b; + /* measure distance between ctx and wl adlls */ + if (mode == TX_DIR) { + a &= ADLL_TX_RES_REG_MASK; + if (a >= ADLL_TAPS_PER_PERIOD) + a -= ADLL_TAPS_PER_PERIOD; + dq_to_dqs_delta[subphy][bit] = + a - (wl_adll[subphy] & WR_LVL_REF_DLY_MASK); + if (dq_to_dqs_delta[subphy][bit] < dq_to_dqs_min_delta) + dq_to_dqs_min_delta = dq_to_dqs_delta[subphy][bit]; + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("%s: dq_to_dqs_delta[%d][%d] %d\n", + __func__, subphy, bit, + dq_to_dqs_delta[subphy][bit])); + } + } + + /* adjust wl on the first pass only */ + if ((mode == TX_DIR) && (loop == 0)) { + /* dqs pbs shift if distance b/w adll is too large */ + if (dq_to_dqs_min_delta < dq_to_dqs_min_delta_threshold) { + /* first calculate the WL in taps */ + wl_tap = ((wl_adll[subphy] >> WR_LVL_REF_DLY_OFFS) & + WR_LVL_REF_DLY_MASK) + + ((wl_adll[subphy] >> WR_LVL_PH_SEL_OFFS) & + WR_LVL_PH_SEL_MASK) * ADLL_TAPS_PER_PHASE; + + /* calc dqs pbs shift */ + dqs_shift[subphy] = + dq_to_dqs_min_delta_threshold - dq_to_dqs_min_delta; + /* check that the WL result have enough taps to reduce */ + if (wl_tap > 0) { + if (wl_tap < dqs_shift[subphy]) + dqs_shift[subphy] = wl_tap-1; + else + dqs_shift[subphy] = dqs_shift[subphy]; + } else { + dqs_shift[subphy] = 0; + } + DEBUG_TAP_TUNING_ENGINE + (DEBUG_LEVEL_INFO, + ("%s: tap tune tx: subphy %d, dqs shifted by %d adll taps, ", + __func__, subphy, dqs_shift[subphy])); + dqs_shift[subphy] = + (dqs_shift[subphy] * PBS_VAL_FACTOR) / pbs_tap_factor0; + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("%d pbs taps\n", dqs_shift[subphy])); + /* check high limit */ + if (dqs_shift[subphy] > MAX_PBS_NUM) + dqs_shift[subphy] = MAX_PBS_NUM; + reg_addr = PBS_TX_PHY_REG(effective_cs, DQSP_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + reg_addr, dqs_shift[subphy]); + reg_addr = PBS_TX_PHY_REG(effective_cs, DQSN_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + reg_addr, dqs_shift[subphy]); + + is_dq_dqs_short[subphy] = DQS_TO_DQ_SHORT; + + new_wl_tap = wl_tap - + (dqs_shift[subphy] * pbs_tap_factor0) / PBS_VAL_FACTOR; + reg_val = (new_wl_tap & WR_LVL_REF_DLY_MASK) | + ((new_wl_tap & + ((WR_LVL_PH_SEL_MASK << WR_LVL_PH_SEL_OFFS) >> 1)) + << 1) | + (wl_adll[subphy] & + ((CTRL_CENTER_DLY_MASK << CTRL_CENTER_DLY_OFFS) | + (CTRL_CENTER_DLY_INV_MASK << CTRL_CENTER_DLY_INV_OFFS))); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + WL_PHY_REG(effective_cs), reg_val); + DEBUG_TAP_TUNING_ENGINE + (DEBUG_LEVEL_INFO, + ("%s: subphy %d, dq_to_dqs_min_delta %d, dqs_shift %d, old wl %d, temp wl %d 0x%08x\n", + __func__, subphy, dq_to_dqs_min_delta, + dqs_shift[subphy], wl_tap, new_wl_tap, + reg_val)); + } + } + dq_to_dqs_min_delta = dq_to_dqs_min_delta_threshold * 2; + } + } + } + + /* deskew dq */ + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + if (mode == RX_DIR) + reg_addr = PBS_RX_BCAST_PHY_REG(effective_cs); + else + reg_addr = PBS_TX_BCAST_PHY_REG(effective_cs); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + DDR_PHY_DATA, reg_addr, new_pbs_per_byte[0]); + } + + /* run training search and get results */ + ddr3_tip_ip_training_wrapper(dev, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, HWS_CONTROL_ELEMENT_ADLL, PARAM_NOT_CARE, + dir, tm->if_act_mask, 0x0, max_win_size - 1, max_win_size - 1, + pattern, EDGE_FPF, CS_SINGLE, PARAM_NOT_CARE, training_result); + + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + /* read training ip results from db */ + for (search_dir = HWS_LOW2HIGH; search_dir <= HWS_HIGH2LOW; search_dir++) { + ddr3_tip_read_training_result(dev, iface, ACCESS_TYPE_UNICAST, + subphy, ALL_BITS_PER_PUP, search_dir, + dir, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, CS_SINGLE, + &(result[subphy][search_dir]), + 1, 0, 0); + + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d mode %d result: " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + effective_cs, iface, subphy, mode, + result[subphy][search_dir][0], + result[subphy][search_dir][1], + result[subphy][search_dir][2], + result[subphy][search_dir][3], + result[subphy][search_dir][4], + result[subphy][search_dir][5], + result[subphy][search_dir][6], + result[subphy][search_dir][7])); + } + + /* calc dq skew impact on vw position */ + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + start_win_diff = 0; + end_win_diff = 0; + limit_div = 0; + if ((GET_LOCK_RESULT(result1[subphy][HWS_LOW2HIGH][bit]) == 1) && + (GET_LOCK_RESULT(result1[subphy][HWS_HIGH2LOW][bit]) == 1) && + (GET_LOCK_RESULT(result[subphy][HWS_LOW2HIGH][bit]) == 1) && + (GET_LOCK_RESULT(result[subphy][HWS_HIGH2LOW][bit]) == 1)) { + curr_start_win = GET_TAP_RESULT(result1[subphy][HWS_LOW2HIGH][bit], + EDGE_1); + curr_end_win = GET_TAP_RESULT(result1[subphy][HWS_HIGH2LOW][bit], + EDGE_1); + upd_curr_start_win = GET_TAP_RESULT(result[subphy][HWS_LOW2HIGH][bit], + EDGE_1); + upd_curr_end_win = GET_TAP_RESULT(result[subphy][HWS_HIGH2LOW][bit], + EDGE_1); + + /* update tx start skew; set rx vw position */ + if ((upd_curr_start_win != 0) && (curr_start_win != 0)) { + if (upd_curr_start_win > curr_start_win) { + start_win_diff = upd_curr_start_win - curr_start_win; + if (mode == TX_DIR) + start_win_diff = + curr_start_win + 64 - upd_curr_start_win; + } else { + start_win_diff = curr_start_win - upd_curr_start_win; + } + limit_div++; + } else { + rx_vw_pos[iface][subphy] = ALIGN_LEFT; + } + + /* update tx end skew; set rx vw position */ + if (((upd_curr_end_win != max_win_size) && (curr_end_win != max_win_size)) || + (mode == TX_DIR)) { + if (upd_curr_end_win > curr_end_win) { + end_win_diff = upd_curr_end_win - curr_end_win; + if (mode == TX_DIR) + end_win_diff = + curr_end_win + 64 - upd_curr_end_win; + } else { + end_win_diff = curr_end_win - upd_curr_end_win; + } + limit_div++; + } else { + rx_vw_pos[iface][subphy] = ALIGN_RIGHT; + } + + /* + * don't care about start in tx mode + * TODO: temporary solution for instability in the start adll search + */ + if (mode == TX_DIR) { + start_win_diff = end_win_diff; + limit_div = 2; + } + + /* + * workaround for false tx measurements in tap tune stage + * tx pbs factor will use rx pbs factor results instead + */ + if ((limit_div != 0) && (mode == RX_DIR)) { + pbs_tap_factor[iface][subphy][bit] = + PBS_VAL_FACTOR * (start_win_diff + end_win_diff) / + (new_pbs_per_byte[subphy] * limit_div); + tmp_pbs_tap_factor[iface][subphy][bit] = + pbs_tap_factor[iface][subphy][bit]; + } else { + pbs_tap_factor[iface][subphy][bit] = + tmp_pbs_tap_factor[iface][subphy][bit]; + } + + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d bit %d sw1 %d sw2 %d " + "ew1 %d ew2 %d sum delta %d, align %d\n", + effective_cs, iface, subphy, bit, + curr_start_win, upd_curr_start_win, + curr_end_win, upd_curr_end_win, + pbs_tap_factor[iface][subphy][bit], + rx_vw_pos[iface][subphy])); + } else { + status = MV_FAIL; + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("tap tuning fail %s cs %d if %d subphy %d bit %d\n", + (mode == RX_DIR) ? "RX" : "TX", effective_cs, iface, + subphy, bit)); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d mode %d result: " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + effective_cs, iface, subphy, mode, + result[subphy][HWS_LOW2HIGH][0], + result[subphy][HWS_LOW2HIGH][1], + result[subphy][HWS_LOW2HIGH][2], + result[subphy][HWS_LOW2HIGH][3], + result[subphy][HWS_LOW2HIGH][4], + result[subphy][HWS_LOW2HIGH][5], + result[subphy][HWS_LOW2HIGH][6], + result[subphy][HWS_LOW2HIGH][7])); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d mode %d result: " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + effective_cs, iface, subphy, mode, + result[subphy][HWS_HIGH2LOW][0], + result[subphy][HWS_HIGH2LOW][1], + result[subphy][HWS_HIGH2LOW][2], + result[subphy][HWS_HIGH2LOW][3], + result[subphy][HWS_HIGH2LOW][4], + result[subphy][HWS_HIGH2LOW][5], + result[subphy][HWS_HIGH2LOW][6], + result[subphy][HWS_HIGH2LOW][7])); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d mode %d result: " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + effective_cs, iface, subphy, mode, + result1[subphy][HWS_LOW2HIGH][0], + result1[subphy][HWS_LOW2HIGH][1], + result1[subphy][HWS_LOW2HIGH][2], + result1[subphy][HWS_LOW2HIGH][3], + result1[subphy][HWS_LOW2HIGH][4], + result1[subphy][HWS_LOW2HIGH][5], + result1[subphy][HWS_LOW2HIGH][6], + result1[subphy][HWS_LOW2HIGH][7])); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("cs %d if %d subphy %d mode %d result: " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + effective_cs, iface, subphy, mode, + result1[subphy][HWS_HIGH2LOW][0], + result1[subphy][HWS_HIGH2LOW][1], + result1[subphy][HWS_HIGH2LOW][2], + result1[subphy][HWS_HIGH2LOW][3], + result1[subphy][HWS_HIGH2LOW][4], + result1[subphy][HWS_HIGH2LOW][5], + result1[subphy][HWS_HIGH2LOW][6], + result1[subphy][HWS_HIGH2LOW][7])); + } + } + } + } + + /* restore cs enable value */ + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + ddr3_tip_if_write(dev, ACCESS_TYPE_UNICAST, iface, DUAL_DUNIT_CFG_REG, + cs_ena_reg_val[iface], MASK_ALL_BITS); + } + + /* restore pbs (set to 0) */ + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + if (mode == RX_DIR) + reg_addr = PBS_RX_BCAST_PHY_REG(effective_cs); + else + reg_addr = PBS_TX_BCAST_PHY_REG(effective_cs); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_UNICAST, + subphy, DDR_PHY_DATA, reg_addr, 0); + } + } + + /* set deskew bias for rx valid window */ + if (mode == RX_DIR) { + /* + * pattern special for rx + * check for rx_vw_pos stat + * - add n pbs taps to every dq to align to left (pbs_max set to (31 - n)) + * - add pbs taps to dqs to align to right + */ + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + if (rx_vw_pos[iface][subphy] == ALIGN_LEFT) { + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, 0, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + PBS_RX_BCAST_PHY_REG(effective_cs), + VW_DESKEW_BIAS); + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("%s: if %d, subphy %d aligned to left\n", + __func__, iface, subphy)); + } else if (rx_vw_pos[iface][subphy] == ALIGN_RIGHT) { + reg_addr = PBS_RX_PHY_REG(effective_cs, DQSP_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, 0, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + reg_addr, VW_DESKEW_BIAS); + reg_addr = PBS_RX_PHY_REG(effective_cs, DQSN_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, 0, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + reg_addr, VW_DESKEW_BIAS); + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("%s: if %d , subphy %d aligned to right\n", + __func__, iface, subphy)); + } + } /* subphy */ + } /* if */ + } else { /* tx mode */ + /* update wl solution */ + if (status == MV_OK) { + for (iface = 0; iface < MAX_INTERFACE_NUM; iface++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, iface); + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + if (is_dq_dqs_short[subphy]) { + wl_tap = ((wl_adll[subphy] >> WR_LVL_REF_DLY_OFFS) & + WR_LVL_REF_DLY_MASK) + + ((wl_adll[subphy] >> WR_LVL_PH_SEL_OFFS) & + WR_LVL_PH_SEL_MASK) * ADLL_TAPS_PER_PHASE; + pbs_tap_factor_avg = (pbs_tap_factor[iface][subphy][0] + + pbs_tap_factor[iface][subphy][1] + + pbs_tap_factor[iface][subphy][2] + + pbs_tap_factor[iface][subphy][3] + + pbs_tap_factor[iface][subphy][4] + + pbs_tap_factor[iface][subphy][5] + + pbs_tap_factor[iface][subphy][6] + + pbs_tap_factor[iface][subphy][7]) / 8; + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("%s: pbs_tap_factor_avg %d\n", + __func__, pbs_tap_factor_avg)); + new_wl_tap = wl_tap - + (dqs_shift[subphy] * pbs_tap_factor_avg) / + PBS_VAL_FACTOR; + /* + * check wraparound due to change in the pbs_tap_factor_avg + * vs the first guess + */ + if (new_wl_tap <= 0) + new_wl_tap = 0; + + reg_val = (new_wl_tap & WR_LVL_REF_DLY_MASK) | + ((new_wl_tap & + ((WR_LVL_PH_SEL_MASK << WR_LVL_PH_SEL_OFFS) >> 1)) + << 1) | + (wl_adll[subphy] & + ((CTRL_CENTER_DLY_MASK << CTRL_CENTER_DLY_OFFS) | + (CTRL_CENTER_DLY_INV_MASK << CTRL_CENTER_DLY_INV_OFFS))); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + WL_PHY_REG(effective_cs), reg_val); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("%s: tap tune tx algorithm final wl:\n", + __func__)); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("%s: subphy %d, dqs pbs %d, old wl %d, final wl %d 0x%08x -> 0x%08x\n", + __func__, subphy, pbs_tap_factor_avg, + wl_tap, new_wl_tap, wl_adll[subphy], + reg_val)); + } + } + } + } else { + /* return to nominal wl */ + for (subphy = 0; subphy < subphy_max; subphy++) { + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_UNICAST, + subphy, DDR_PHY_DATA, WL_PHY_REG(effective_cs), + wl_adll[subphy]); + DEBUG_TAP_TUNING_ENGINE(DEBUG_LEVEL_INFO, + ("%s: tap tune failed; return to nominal wl\n", + __func__)); + reg_addr = PBS_TX_PHY_REG(effective_cs, DQSP_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_UNICAST, + subphy, DDR_PHY_DATA, reg_addr, 0); + reg_addr = PBS_TX_PHY_REG(effective_cs, DQSN_PAD); + ddr3_tip_bus_write(dev, ACCESS_TYPE_UNICAST, iface, ACCESS_TYPE_UNICAST, + subphy, DDR_PHY_DATA, reg_addr, 0); + } + } + } + + return status; +} + +/* receiver duty cycle flow */ +#define DDR_PHY_JIRA_ENABLE +int mv_ddr4_receiver_calibration(u8 dev_num) +{ + u32 if_id, subphy_num; + u32 vref_idx, dq_idx, pad_num = 0; + u8 dq_vref_start_win[MAX_INTERFACE_NUM][MAX_BUS_NUM][RECEIVER_DC_MAX_COUNT]; + u8 dq_vref_end_win[MAX_INTERFACE_NUM][MAX_BUS_NUM][RECEIVER_DC_MAX_COUNT]; + u8 c_vref[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 valid_win_size[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 c_opt_per_bus[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 valid_vref_cnt[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 valid_vref_ptr[MAX_INTERFACE_NUM][MAX_BUS_NUM][RECEIVER_DC_MAX_COUNT]; + u8 center_adll[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 center_vref[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 pbs_res_per_bus[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + u16 lambda_per_dq[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + u8 dqs_pbs = 0, const_pbs; + int tap_tune_passed = 0; + struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get(); + enum hws_result *flow_result = ddr3_tip_get_result_ptr(training_stage); + u8 subphy_max = ddr3_tip_dev_attr_get(dev_num, MV_ATTR_OCTET_PER_INTERFACE); +#ifdef DDR_PHY_JIRA_ENABLE + u32 dqs_pbs_jira56[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u8 delta = 0; +#endif + unsigned int max_cs = mv_ddr_cs_num_get(); + u32 ctr_x[4], pbs_temp[4]; + u16 cs_index = 0, pbs_rx_avg, lambda_avg; + int status; + + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, ("Starting ddr4 dc calibration training stage\n")); + + vdq_tv = 0; + duty_cycle = 0; + + /* reset valid vref counter per if and subphy */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) + for (subphy_num = 0; subphy_num < MAX_BUS_NUM; subphy_num++) + valid_vref_cnt[if_id][subphy_num] = 0; + + /* calculate pbs-adll tap tuning */ + /* reset special pattern configuration to re-run this stage */ + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + DDR_PHY_DATA, 0x5f + effective_cs * 0x10, 0x0); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + DDR_PHY_DATA, 0x54 + effective_cs * 0x10, 0x0); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + DDR_PHY_DATA, 0x55 + effective_cs * 0x10, 0x0); + if (status != MV_OK) + return status; + +#ifdef DDR_PHY_JIRA_ENABLE + if (effective_cs != 0) { + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + status = ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, subphy_num, + DDR_PHY_DATA, 0x54 + 0 * 0x10, + &dqs_pbs_jira56[if_id][subphy_num]); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, 0x54 + 0 * 0x10, 0x0); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, 0x55 + 0 * 0x10, 0x0); + if (status != MV_OK) + return status; + } + } + } +#endif + + if (mv_ddr4_tap_tuning(dev_num, lambda_per_dq, RX_DIR) == MV_OK) + tap_tune_passed = 1; + + /* main loop for 2d scan (low_to_high voltage scan) */ + for (duty_cycle = RECEIVER_DC_MIN_RANGE; + duty_cycle <= RECEIVER_DC_MAX_RANGE; + duty_cycle += RECEIVER_DC_STEP_SIZE) { + /* set new receiver dc training value in dram */ + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + VREF_BCAST_PHY_REG(effective_cs), duty_cycle); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + VREF_PHY_REG(effective_cs, DQSP_PAD), duty_cycle); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + VREF_PHY_REG(effective_cs, DQSN_PAD), duty_cycle); + if (status != MV_OK) + return status; + + if (tap_tune_passed == 0) { + if (mv_ddr4_tap_tuning(dev_num, lambda_per_dq, RX_DIR) == MV_OK) { + tap_tune_passed = 1; + } else { + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("rc, tap tune failed inside calibration\n")); + continue; + } + } + + if (mv_ddr4_centralization(dev_num, lambda_per_dq, c_opt_per_bus, pbs_res_per_bus, + valid_win_size, RX_DIR, vdq_tv, duty_cycle) != MV_OK) { + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("error: ddr4 centralization failed (duty_cycle %d)!!!\n", duty_cycle)); + if (debug_mode == 0) + break; + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + if (valid_win_size[if_id][subphy_num] > 8) { + /* window is valid; keep current duty_cycle value and increment counter */ + vref_idx = valid_vref_cnt[if_id][subphy_num]; + valid_vref_ptr[if_id][subphy_num][vref_idx] = duty_cycle; + valid_vref_cnt[if_id][subphy_num]++; + c_vref[if_id][subphy_num] = c_opt_per_bus[if_id][subphy_num]; + /* set 0 for possible negative values */ + dq_vref_start_win[if_id][subphy_num][vref_idx] = + c_vref[if_id][subphy_num] + 1 - valid_win_size[if_id][subphy_num] / 2; + dq_vref_start_win[if_id][subphy_num][vref_idx] = + (valid_win_size[if_id][subphy_num] % 2 == 0) ? + dq_vref_start_win[if_id][subphy_num][vref_idx] : + dq_vref_start_win[if_id][subphy_num][vref_idx] - 1; + dq_vref_end_win[if_id][subphy_num][vref_idx] = + c_vref[if_id][subphy_num] + valid_win_size[if_id][subphy_num] / 2; + } + } /* subphy */ + } /* if */ + } /* duty_cycle */ + + if (tap_tune_passed == 0) { + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("%s: tap tune not passed on any duty_cycle value\n", __func__)); + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + /* report fail for all active interfaces; multi-interface support - tbd */ + flow_result[if_id] = TEST_FAILED; + } + + return MV_FAIL; + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("calculating center of mass for subphy %d, valid window size %d\n", + subphy_num, valid_win_size[if_id][subphy_num])); + if (valid_vref_cnt[if_id][subphy_num] > 0) { + rx_eye_hi_lvl[subphy_num] = + valid_vref_ptr[if_id][subphy_num][valid_vref_cnt[if_id][subphy_num] - 1]; + rx_eye_lo_lvl[subphy_num] = valid_vref_ptr[if_id][subphy_num][0]; + /* calculate center of mass sampling point (t, v) for each subphy */ + status = mv_ddr4_center_of_mass_calc(dev_num, if_id, subphy_num, RX_DIR, + dq_vref_start_win[if_id][subphy_num], + dq_vref_end_win[if_id][subphy_num], + valid_vref_ptr[if_id][subphy_num], + valid_vref_cnt[if_id][subphy_num], + ¢er_vref[if_id][subphy_num], + ¢er_adll[if_id][subphy_num]); + if (status != MV_OK) + return status; + + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("center of mass results: vref %d, adll %d\n", + center_vref[if_id][subphy_num], center_adll[if_id][subphy_num])); + } else { + DEBUG_CALIBRATION(DEBUG_LEVEL_ERROR, + ("%s: no valid window found for cs %d, subphy %d\n", + __func__, effective_cs, subphy_num)); + return MV_FAIL; + } + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_UNICAST, subphy_num, DDR_PHY_DATA, + VREF_BCAST_PHY_REG(effective_cs), + center_vref[if_id][subphy_num]); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_UNICAST, subphy_num, DDR_PHY_DATA, + VREF_PHY_REG(effective_cs, DQSP_PAD), + center_vref[if_id][subphy_num]); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_UNICAST, subphy_num, DDR_PHY_DATA, + VREF_PHY_REG(effective_cs, DQSN_PAD), + center_vref[if_id][subphy_num]); + if (status != MV_OK) + return status; + + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, ("final dc %d\n", center_vref[if_id][subphy_num])); + } + + /* run centralization again with optimal vref to update global structures */ + mv_ddr4_centralization(dev_num, lambda_per_dq, c_opt_per_bus, pbs_res_per_bus, valid_win_size, + RX_DIR, 0, center_vref[if_id][0]); + + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + + const_pbs = 0xa; + mv_ddr4_dqs_reposition(RX_DIR, lambda_per_dq[if_id][subphy_num], + pbs_res_per_bus[if_id][subphy_num], 0x0, + ¢er_adll[if_id][subphy_num], &dqs_pbs); + + /* dq pbs update */ + for (dq_idx = 0; dq_idx < 8 ; dq_idx++) { + pad_num = dq_map_table[dq_idx + + subphy_num * BUS_WIDTH_IN_BITS + + if_id * BUS_WIDTH_IN_BITS * subphy_max]; + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + 0x50 + pad_num + effective_cs * 0x10, + const_pbs + pbs_res_per_bus[if_id][subphy_num][dq_idx]); + if (status != MV_OK) + return status; + } + + /* dqs pbs update */ + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, subphy_num, + DDR_PHY_DATA, 0x54 + effective_cs * 0x10, dqs_pbs); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, subphy_num, + DDR_PHY_DATA, 0x55 + effective_cs * 0x10, dqs_pbs); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + CRX_PHY_REG(effective_cs), + center_adll[if_id][subphy_num]); + if (status != MV_OK) + return status; + +#ifdef DDR_PHY_JIRA_ENABLE + if (effective_cs != 0) { + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, 0x54 + 0 * 0x10, + dqs_pbs_jira56[if_id][subphy_num]); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, 0x55 + 0 * 0x10, + dqs_pbs_jira56[if_id][subphy_num]); + if (status != MV_OK) + return status; + } +#endif + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + /* report pass for all active interfaces; multi-interface support - tbd */ + flow_result[if_id] = TEST_SUCCESS; + } + +#ifdef DDR_PHY_JIRA_ENABLE + if (effective_cs == (max_cs - 1)) { + /* adjust dqs to be as cs0 */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_IF_ACTIVE(tm->if_act_mask, if_id); + for (subphy_num = 0; subphy_num < subphy_max; subphy_num++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy_num); + pbs_rx_avg = 0; + /* find average of all pbs of dqs and read ctr_x */ + for (cs_index = 0; cs_index < max_cs; cs_index++) { + status = ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + 0x54 + cs_index * 0x10, + &pbs_temp[cs_index]); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + 0x3 + cs_index * 0x4, + &ctr_x[cs_index]); + if (status != MV_OK) + return status; + + pbs_rx_avg = pbs_rx_avg + pbs_temp[cs_index]; + } + + pbs_rx_avg = pbs_rx_avg / max_cs; + + /* update pbs and ctr_x */ + lambda_avg = (lambda_per_dq[if_id][subphy_num][0] + + lambda_per_dq[if_id][subphy_num][1] + + lambda_per_dq[if_id][subphy_num][2] + + lambda_per_dq[if_id][subphy_num][3] + + lambda_per_dq[if_id][subphy_num][4] + + lambda_per_dq[if_id][subphy_num][5] + + lambda_per_dq[if_id][subphy_num][6] + + lambda_per_dq[if_id][subphy_num][7]) / 8; + + for (cs_index = 0; cs_index < max_cs; cs_index++) { + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, + 0, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + 0x54 + cs_index * 0x10, pbs_rx_avg); + if (status != MV_OK) + return status; + + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, + 0, ACCESS_TYPE_UNICAST, + subphy_num, DDR_PHY_DATA, + 0x55 + cs_index * 0x10, pbs_rx_avg); + if (status != MV_OK) + return status; + + /* update */ + if (pbs_rx_avg >= pbs_temp[cs_index]) { + delta = ((pbs_rx_avg - pbs_temp[cs_index]) * lambda_avg) / + PBS_VAL_FACTOR; + if (ctr_x[cs_index] >= delta) { + ctr_x[cs_index] = ctr_x[cs_index] - delta; + } else { + ctr_x[cs_index] = 0; + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("jira ddrphy56 extend fix(-) required %d\n", + delta)); + } + } else { + delta = ((pbs_temp[cs_index] - pbs_rx_avg) * lambda_avg) / + PBS_VAL_FACTOR; + if ((ctr_x[cs_index] + delta) > 32) { + ctr_x[cs_index] = 32; + DEBUG_CALIBRATION(DEBUG_LEVEL_INFO, + ("jira ddrphy56 extend fix(+) required %d\n", + delta)); + } else { + ctr_x[cs_index] = (ctr_x[cs_index] + delta); + } + } + status = ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, subphy_num, DDR_PHY_DATA, + CRX_PHY_REG(effective_cs), + ctr_x[cs_index]); + if (status != MV_OK) + return status; + } + } + } + } +#endif + + return MV_OK; +} + +#define MAX_LOOPS 2 /* maximum number of loops to get to solution */ +#define LEAST_SIGNIFICANT_BYTE_MASK 0xff +#define VW_SUBPHY_LIMIT_MIN 0 +#define VW_SUBPHY_LIMIT_MAX 127 +#define MAX_PBS_NUM 31 /* TODO: added by another patch */ +enum{ + LOCKED, + UNLOCKED +}; +enum { + PASS, + FAIL +}; + +int mv_ddr4_dm_tuning(u32 cs, u16 (*pbs_tap_factor)[MAX_BUS_NUM][BUS_WIDTH_IN_BITS]) +{ + struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get(); + enum hws_training_ip_stat training_result; + enum hws_training_result result_type = RESULT_PER_BIT; + enum hws_search_dir search_dir; + enum hws_dir dir = OPER_WRITE; + int vw_sphy_hi_diff = 0; + int vw_sphy_lo_diff = 0; + int x, y; + int status; + unsigned int a, b, c; + u32 ctx_vector[MAX_BUS_NUM]; + u32 subphy, bit, pattern; + u32 *result[MAX_BUS_NUM][HWS_SEARCH_DIR_LIMIT]; + u32 max_win_size = MAX_WINDOW_SIZE_TX; + u32 dm_lambda[MAX_BUS_NUM] = {0}; + u32 loop; + u32 adll_tap; + u32 dm_pbs, max_pbs; + u32 dq_pbs[BUS_WIDTH_IN_BITS]; + u32 new_dq_pbs[BUS_WIDTH_IN_BITS]; + u32 dq, pad; + u32 dq_pbs_diff; + u32 byte_center, dm_center; + u32 idx, reg_val; + u32 dm_pad = mv_ddr_dm_pad_get(); + u8 subphy_max = ddr3_tip_dev_attr_get(0, MV_ATTR_OCTET_PER_INTERFACE); + u8 dm_vw_vector[MAX_BUS_NUM * ADLL_TAPS_PER_PERIOD]; + u8 vw_sphy_lo_lmt[MAX_BUS_NUM]; + u8 vw_sphy_hi_lmt[MAX_BUS_NUM]; + u8 dm_status[MAX_BUS_NUM]; + + /* init */ + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + dm_status[subphy] = UNLOCKED; + for (bit = 0 ; bit < BUS_WIDTH_IN_BITS; bit++) + dm_lambda[subphy] += pbs_tap_factor[0][subphy][bit]; + dm_lambda[subphy] /= BUS_WIDTH_IN_BITS; + } + + /* get algorithm's adll result */ + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + ddr3_tip_bus_read(0, 0, ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + CTX_PHY_REG(cs), ®_val); + ctx_vector[subphy] = reg_val; + } + + for (loop = 0; loop < MAX_LOOPS; loop++) { + for (subphy = 0; subphy < subphy_max; subphy++) { + vw_sphy_lo_lmt[subphy] = VW_SUBPHY_LIMIT_MIN; + vw_sphy_hi_lmt[subphy] = VW_SUBPHY_LIMIT_MAX; + for (adll_tap = 0; adll_tap < ADLL_TAPS_PER_PERIOD; adll_tap++) { + idx = subphy * ADLL_TAPS_PER_PERIOD + adll_tap; + dm_vw_vector[idx] = PASS; + } + } + + /* get valid window of dm signal */ + mv_ddr_dm_vw_get(PATTERN_ZERO, cs, dm_vw_vector); + mv_ddr_dm_vw_get(PATTERN_ONE, cs, dm_vw_vector); + + /* get vw for dm disable */ + pattern = MV_DDR_IS_64BIT_DRAM_MODE(tm->bus_act_mask) ? 73 : 23; + ddr3_tip_ip_training_wrapper(0, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, HWS_CONTROL_ELEMENT_ADLL, PARAM_NOT_CARE, + dir, tm->if_act_mask, 0x0, max_win_size - 1, max_win_size - 1, pattern, + EDGE_FPF, CS_SINGLE, PARAM_NOT_CARE, &training_result); + + /* find skew of dm signal vs. dq data bits using its valid window */ + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + CTX_PHY_REG(cs), ctx_vector[subphy]); + + for (search_dir = HWS_LOW2HIGH; search_dir <= HWS_HIGH2LOW; search_dir++) { + ddr3_tip_read_training_result(0, 0, ACCESS_TYPE_UNICAST, subphy, + ALL_BITS_PER_PUP, search_dir, dir, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, CS_SINGLE, + &(result[subphy][search_dir]), + 1, 0, 0); + DEBUG_DM_TUNING(DEBUG_LEVEL_INFO, + ("dm cs %d if %d subphy %d result: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + cs, 0, subphy, + result[subphy][search_dir][0], + result[subphy][search_dir][1], + result[subphy][search_dir][2], + result[subphy][search_dir][3], + result[subphy][search_dir][4], + result[subphy][search_dir][5], + result[subphy][search_dir][6], + result[subphy][search_dir][7])); + } + + if (dm_status[subphy] == LOCKED) + continue; + + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + result[subphy][HWS_LOW2HIGH][bit] &= LEAST_SIGNIFICANT_BYTE_MASK; + result[subphy][HWS_HIGH2LOW][bit] &= LEAST_SIGNIFICANT_BYTE_MASK; + + if (result[subphy][HWS_LOW2HIGH][bit] > vw_sphy_lo_lmt[subphy]) + vw_sphy_lo_lmt[subphy] = result[subphy][HWS_LOW2HIGH][bit]; + + if (result[subphy][HWS_HIGH2LOW][bit] < vw_sphy_hi_lmt[subphy]) + vw_sphy_hi_lmt[subphy] = result[subphy][HWS_HIGH2LOW][bit]; + } + + DEBUG_DM_TUNING(DEBUG_LEVEL_INFO, + ("loop %d, dm subphy %d, vw %d, %d\n", loop, subphy, + vw_sphy_lo_lmt[subphy], vw_sphy_hi_lmt[subphy])); + + idx = subphy * ADLL_TAPS_PER_PERIOD; + status = mv_ddr_dm_to_dq_diff_get(vw_sphy_hi_lmt[subphy], vw_sphy_lo_lmt[subphy], + &dm_vw_vector[idx], &vw_sphy_hi_diff, &vw_sphy_lo_diff); + if (status != MV_OK) + return MV_FAIL; + DEBUG_DM_TUNING(DEBUG_LEVEL_INFO, + ("vw_sphy_lo_diff %d, vw_sphy_hi_diff %d\n", + vw_sphy_lo_diff, vw_sphy_hi_diff)); + + /* dm is the strongest signal */ + if ((vw_sphy_hi_diff >= 0) && + (vw_sphy_lo_diff >= 0)) { + dm_status[subphy] = LOCKED; + } else if ((vw_sphy_hi_diff >= 0) && + (vw_sphy_lo_diff < 0) && + (loop == 0)) { /* update dm only */ + ddr3_tip_bus_read(0, 0, ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, dm_pad), &dm_pbs); + x = -vw_sphy_lo_diff; /* get positive x */ + a = (unsigned int)x * PBS_VAL_FACTOR; + b = dm_lambda[subphy]; + if (round_div(a, b, &c) != MV_OK) + return MV_FAIL; + dm_pbs += (u32)c; + dm_pbs = (dm_pbs > MAX_PBS_NUM) ? MAX_PBS_NUM : dm_pbs; + ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, + subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, dm_pad), dm_pbs); + } else if ((vw_sphy_hi_diff < 0) && + (vw_sphy_lo_diff >= 0) && + (loop == 0)) { /* update dq and c_opt */ + max_pbs = 0; + for (dq = 0; dq < BUS_WIDTH_IN_BITS; dq++) { + idx = dq + subphy * BUS_WIDTH_IN_BITS; + pad = dq_map_table[idx]; + ddr3_tip_bus_read(0, 0, ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, pad), ®_val); + dq_pbs[dq] = reg_val; + x = -vw_sphy_hi_diff; /* get positive x */ + a = (unsigned int)x * PBS_VAL_FACTOR; + b = pbs_tap_factor[0][subphy][dq]; + if (round_div(a, b, &c) != MV_OK) + return MV_FAIL; + new_dq_pbs[dq] = dq_pbs[dq] + (u32)c; + if (max_pbs < new_dq_pbs[dq]) + max_pbs = new_dq_pbs[dq]; + } + + dq_pbs_diff = (max_pbs > MAX_PBS_NUM) ? (max_pbs - MAX_PBS_NUM) : 0; + for (dq = 0; dq < BUS_WIDTH_IN_BITS; dq++) { + idx = dq + subphy * BUS_WIDTH_IN_BITS; + reg_val = new_dq_pbs[dq] - dq_pbs_diff; + if (reg_val < 0) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("unexpected negative value found\n")); + return MV_FAIL; + } + pad = dq_map_table[idx]; + ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, + ACCESS_TYPE_UNICAST, subphy, + DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, pad), + reg_val); + } + + a = dm_lambda[subphy]; + b = dq_pbs_diff * PBS_VAL_FACTOR; + if (b > 0) { + if (round_div(a, b, &c) != MV_OK) + return MV_FAIL; + dq_pbs_diff = (u32)c; + } + + x = (int)ctx_vector[subphy]; + if (x < 0) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("unexpected negative value found\n")); + return MV_FAIL; + } + y = (int)dq_pbs_diff; + if (y < 0) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("unexpected negative value found\n")); + return MV_FAIL; + } + x += (y + vw_sphy_hi_diff) / 2; + x %= ADLL_TAPS_PER_PERIOD; + ctx_vector[subphy] = (u32)x; + } else if (((vw_sphy_hi_diff < 0) && (vw_sphy_lo_diff < 0)) || + (loop == 1)) { /* dm is the weakest signal */ + /* update dq and c_opt */ + dm_status[subphy] = LOCKED; + byte_center = (vw_sphy_lo_lmt[subphy] + vw_sphy_hi_lmt[subphy]) / 2; + x = (int)byte_center; + if (x < 0) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("unexpected negative value found\n")); + return MV_FAIL; + } + x += (vw_sphy_hi_diff - vw_sphy_lo_diff) / 2; + if (x < 0) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("unexpected negative value found\n")); + return MV_FAIL; + } + dm_center = (u32)x; + + if (byte_center > dm_center) { + max_pbs = 0; + for (dq = 0; dq < BUS_WIDTH_IN_BITS; dq++) { + pad = dq_map_table[dq + subphy * BUS_WIDTH_IN_BITS]; + ddr3_tip_bus_read(0, 0, ACCESS_TYPE_UNICAST, + subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, pad), + ®_val); + dq_pbs[dq] = reg_val; + a = (byte_center - dm_center) * PBS_VAL_FACTOR; + b = pbs_tap_factor[0][subphy][dq]; + if (round_div(a, b, &c) != MV_OK) + return MV_FAIL; + new_dq_pbs[dq] = dq_pbs[dq] + (u32)c; + if (max_pbs < new_dq_pbs[dq]) + max_pbs = new_dq_pbs[dq]; + } + + dq_pbs_diff = (max_pbs > MAX_PBS_NUM) ? (max_pbs - MAX_PBS_NUM) : 0; + for (int dq = 0; dq < BUS_WIDTH_IN_BITS; dq++) { + idx = dq + subphy * BUS_WIDTH_IN_BITS; + pad = dq_map_table[idx]; + reg_val = new_dq_pbs[dq] - dq_pbs_diff; + if (reg_val < 0) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("unexpected negative value found\n")); + return MV_FAIL; + } + ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, pad), + reg_val); + } + ctx_vector[subphy] = dm_center % ADLL_TAPS_PER_PERIOD; + } else { + ddr3_tip_bus_read(0, 0, ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, dm_pad), &dm_pbs); + a = (dm_center - byte_center) * PBS_VAL_FACTOR; + b = dm_lambda[subphy]; + if (round_div(a, b, &c) != MV_OK) + return MV_FAIL; + dm_pbs += (u32)c; + ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, + ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + PBS_TX_PHY_REG(cs, dm_pad), dm_pbs); + } + } else { + /* below is the check whether dm signal per subphy converged or not */ + } + } + } + + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + ddr3_tip_bus_write(0, ACCESS_TYPE_UNICAST, 0, ACCESS_TYPE_UNICAST, subphy, DDR_PHY_DATA, + CTX_PHY_REG(cs), ctx_vector[subphy]); + } + + for (subphy = 0; subphy < subphy_max; subphy++) { + VALIDATE_BUS_ACTIVE(tm->bus_act_mask, subphy); + if (dm_status[subphy] != LOCKED) { + DEBUG_DM_TUNING(DEBUG_LEVEL_ERROR, + ("no convergence for dm signal[%u] found\n", subphy)); + return MV_FAIL; + } + } + + return MV_OK; +} +void refresh(void) +{ + u32 data_read[MAX_INTERFACE_NUM]; + ddr3_tip_if_read(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG, data_read, MASK_ALL_BITS); + + /* Refresh Command for CS0*/ + ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG, (0 << 26), (3 << 26)); + ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, SDRAM_OP_REG, 0xe02, 0xf1f); + if (ddr3_tip_if_polling(0, ACCESS_TYPE_UNICAST, 0, 0, 0x1f, SDRAM_OP_REG, MAX_POLLING_ITERATIONS) != MV_OK) + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, ("DDR3 poll failed")); + + /* Refresh Command for CS1*/ + ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG, (1 << 26), (3 << 26)); + ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, SDRAM_OP_REG, 0xd02, 0xf1f); + if (ddr3_tip_if_polling(0, ACCESS_TYPE_UNICAST, 0, 0, 0x1f, SDRAM_OP_REG, MAX_POLLING_ITERATIONS) != MV_OK) + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, ("DDR3 poll failed")); + + /* Restore Register*/ + ddr3_tip_if_write(0, ACCESS_TYPE_UNICAST, 0, ODPG_DATA_CTRL_REG, data_read[0] , MASK_ALL_BITS); +} +#endif /* CONFIG_DDR4 */ |