/** * @file sdrv_sdramc.c * @brief sdrv sdramc driver * * @copyright Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. */ #include #include #include #include #ifndef CONFIG_SDRAM_BASE #define CONFIG_SDRAM_BASE 0x30000000u #endif #if !CONFIG_WORK_ON_Z1 #define SDRAMC_DLL_LOCK_TIMEOUT (1000) #else #define SDRAMC_DLL_LOCK_TIMEOUT (1) #endif #define SDRAMC_PATTERN_LENS (16u) /* If the width is 32bits, the value is 4u */ #define SDRAMC_WIDTH (4u) #define TRAINING_STEPS (32u) #define WIN_MAX_SUCCESS 33u #if SDRAMC_WIDTH == 4 typedef uint32_t sdram_data_t; #define training_write(val, reg) writel(val, reg) #define training_read(reg) readl(reg) //32bit bl=8 static const uint8_t training_patten[SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = { 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, }; static const uint8_t training_patten_trans[SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, }; #elif SDRAMC_WIDTH == 2 #define training_write(val, reg) writew(val, reg) #define training_read(reg) readw(reg) typedef uint16_t sdram_data_t; //16bit bl=8 static const uint8_t training_patten[SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = { 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, }; static const uint8_t training_patten_trans[SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, }; #elif SDRAMC_WIDTH == 1 #define training_write(val, reg) writeb(val, reg) #define training_read(reg) readb(reg) typedef uint8_t sdram_data_t; //8bit bl=8 static uint8_t training_patten[SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = { 0x55u, 0xAAu, 0x55u, 0xAAu, 0xAAu, 0x55u, 0xAAu, 0x55u, 0xAAu, 0x55u, 0xAAu, 0x55u, 0x55u, 0xAAu, 0x55u, 0xAAu, }; static uint8_t training_patten_trans[SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = { 0x55u, 0xAAu, 0x55u, 0xAAu, 0xAAu, 0x55u, 0xAAu, 0x55u, 0xAAu, 0x55u, 0xAAu, 0x55u, 0x55u, 0xAAu, 0x55u, 0xAAu, }; #endif static uint8_t training_data[TRAINING_STEPS][SDRAMC_WIDTH * SDRAMC_PATTERN_LENS] = {0u} ; struct sd_para cs_p; /** * @brief save sdramc parameters * @param [in] cs get sdram parameters */ void sdrv_set_sd_para(struct sd_para *cs) { if (cs != NULL) { cs_p.column_start = cs->column_start; cs_p.column_bits = cs->column_bits; cs_p.cs_start = cs->cs_start; cs_p.cs_bits = cs->cs_bits; cs_p.bank_start = cs->bank_start; cs_p.bank_bits = cs->bank_bits; cs_p.row_start = cs->row_start; cs_p.row_bits = cs->row_bits; } } /** * @brief get sdramc parameters * @retval sdram parameters */ struct sd_para *sdrv_get_sd_para(void) { return &cs_p; } static inline __ALWAYS_INLINE uint32_t log2_uint(uint32_t val) { if (val == 0u) return 0u; // undefined return (sizeof(val) * 8u) - 1u - __builtin_clz(val); } /** * @brief Matrix reverse to matrix_array_trans array * @param [in] matrix_array raw data * @param [in] matrix_array_trans get reverse data from raw data * @param [in] row Matrix row number * @param [in] column Matrix column number */ static void sdrv_matrix_transpose(uint8_t *matrix_array, uint8_t *matrix_array_trans, uint32_t row, uint32_t column) { for (uint32_t i = 0; i < row; i++) { for (uint32_t j = 0; j < column; j++) { *(matrix_array_trans + column * i + j) = *(matrix_array + row * j + i); } } } /** * @brief sdramc dll config * @param [in] base sdramc base register address * @param [in] value dll config set value * @param [in] num dll serial number dll0~3 * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_dll_config(addr_t base, uint8_t value, uint8_t num) { uint32_t reg; int32_t ret; addr_t dll_addr; addr_t dll_status_addr; dll_addr = SDRAMC_DLL0 + num * 0x4u; dll_status_addr = SDRAMC_DLL_STATUS0 + num * 0x4u; reg = 0u; ret = SDRV_STATUS_OK; ddr_writel(reg, base + dll_addr); reg = value << SDRAMC_DLL0_SLVTGRT_LSB; reg |= SDRAMC_DLL0_DLLEN;//enable dll ddr_writel(reg, base + dll_addr); int32_t j = SDRAMC_DLL_LOCK_TIMEOUT; while (j) { reg = ddr_readl(base + dll_status_addr); if (reg & SDRAMC_DLL_STATUS0_SLVLOCK) { ssdk_printf(SSDK_DEBUG, "sdramc dll%d locked!\r\n", num); break; } j--; } if (j == 0) { ssdk_printf(SSDK_ERR, "sdramc dll%d lock failed!\r\n", num); ret = SDRV_STATUS_SDRAMC_DLL_LOCK_ERR; } return ret; } /** * @brief sdramc config all dll, maximal DLL0-3 * @param [in] base sdramc base register address * @param [in] value dll config set value * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_dll_config_all(addr_t base, uint8_t value) { int32_t ret = SDRV_STATUS_FAIL; /* Config DLL0-3 */ for (uint32_t i = 0; i < 4u; i++) { ret = sdramc_dll_config(base, value, i); if(ret != SDRV_STATUS_OK){ break; } } return ret; } /** * @brief ddr or sdr mode dump training windows result for debug * @param [in] training_win_ddr record training window data * @param [in] conf sdr one sets of data, ddr two sets of data * @param [in] ddr_mode is ddr mode not not */ static void dump_training_win_result(uint32_t (*training_win_ddr)[2][4][2], sdram_config_t *conf, uint32_t times, bool ddr_mode) { SDRAMC_CTL_DATA_DEBUG("cs gate(ddr) dll win\r\n"); for (uint32_t i = 0; i < conf->cs_num ; i++) { for (uint32_t j = 0; j < times; j++) { for (uint32_t k = 0; k < conf->dq_width / 8u; k++) { for (uint32_t l = 0; l < 2u; l++) { SDRAMC_CTL_DATA_DEBUG("[%d][%d][%d][%d] = %d ", i, j, k, l, training_win_ddr[i][j][k][l]); } SDRAMC_CTL_DATA_DEBUG("\r\n"); } SDRAMC_CTL_DATA_DEBUG("\r\n"); } SDRAMC_CTL_DATA_DEBUG("\r\n"); } } /** * @brief training success or failed result dump for debug * @param [in] pass_win_min_l2r lest to right success times and max value 32 * @param [in] times dll training success times */ static void sdrv_training_result_dump(uint32_t pass_win_min_l2r, uint8_t times) { SDRAMC_CTL_DATA_DEBUG("min_l2r = %d \r\n", pass_win_min_l2r); if (pass_win_min_l2r != WIN_MAX_SUCCESS && times == SDRAMC_WIDTH) { SDRAMC_CTL_DATA_DEBUG("failed times = %d\r\nsuccess times = %d \r\n", TRAINING_STEPS - pass_win_min_l2r, pass_win_min_l2r); } else { SDRAMC_CTL_DATA_DEBUG("failed times = %d\r\nsuccess times = %d \r\n", TRAINING_STEPS, TRAINING_STEPS - TRAINING_STEPS); } #ifdef ENABLE_SDRAMC_CTL_DATA_DEBUG ssdk_printf(SSDK_CRIT, "hexdump training data \r\n"); hexdump8_ex(training_data, TRAINING_STEPS * SDRAMC_WIDTH * SDRAMC_PATTERN_LENS, (uint64_t)((addr_t)training_data)); #endif } /** * @brief debug windows info * @param [in] dll_l_r record max left windows to min right windows value */ static void training_maxl2minr_dump(uint32_t (*dll_l_r)[2]) { for (uint32_t i = 0; i < 4u ; i++) { for (uint32_t j = 0; j < 2u; j++) { SDRAMC_CTL_DATA_DEBUG("l_r[%d][%d] = %d ", i, j, dll_l_r[i][j]); } SDRAMC_CTL_DATA_DEBUG("\r\n"); } } /** * @brief record training result and fill training win data * @param [in] ddr_mode ddr or sdr mode * @param [in] ddr_gate_index ddr: index 0 or 1, sdr: index 0 * @param [in] gate_val ddr gate value * @param [in] training_win_data_ddr sdr or ddr result * @param [in] cs_num cs number * @param [in] pass_win_min_l2r record min left to min right window value * @param [in] pass_win_l training pass window min left value * @param [in] pass_win_r training pass window max right value * @param [in] j dll0~4 index */ static void sdramc_getwin(bool ddr_mode, uint32_t ddr_gate_index, uint32_t (*gate_val)[2], uint32_t (*training_win_data_ddr)[2][4][2], uint32_t cs_num, uint32_t pass_win_min_l2r, uint32_t pass_win_l, uint32_t pass_win_r, uint32_t j) { SDRAMC_CTL_DATA_DEBUG("pass_win_min_l2r = %d \r\n", pass_win_min_l2r); if (ddr_mode == true) { if (gate_val != NULL) { gate_val[ddr_gate_index][1] = MIN(gate_val[ddr_gate_index][1], pass_win_min_l2r); } if (training_win_data_ddr != NULL) { training_win_data_ddr[cs_num][ddr_gate_index][j][0] = pass_win_l; training_win_data_ddr[cs_num][ddr_gate_index][j][1] = pass_win_r; } } else { if (training_win_data_ddr != NULL) { training_win_data_ddr[cs_num][0][j][0] = pass_win_l; training_win_data_ddr[cs_num][0][j][1] = pass_win_r; } } } /** * @brief record training result and fill training win data * @param [in] base sdramc base register address * @param [in] ddr_gate_index ddr: index 0 or 1, sdr: index 0 * @param [in] gate_val ddr gate value * @param [in] ddr_mode ddr or sdr mode * @param [in] training_win_data_ddr sdr or ddr result * @param [in] cs_num cs number * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_dll_training(addr_t base, uint32_t ddr_gate_index, uint32_t (*gate_val)[2], bool ddr_mode, uint32_t (*training_win_data_ddr)[2][4][2], uint32_t cs_num) { sdram_data_t *p_pattern; sdram_data_t *p_data; bool data_cmp_res; bool training_succ[SDRAMC_WIDTH] = {false}; bool training_end[SDRAMC_WIDTH] = {false}; uint32_t pass_win_l[SDRAMC_WIDTH] = {0u}; uint32_t pass_win_r[SDRAMC_WIDTH] = {0u}; uint8_t read_data[SDRAMC_PATTERN_LENS * SDRAMC_WIDTH] = {0}; uint32_t pass_win_min_l2r = WIN_MAX_SUCCESS; uint32_t save_pass_win_l[SDRAMC_WIDTH] = {0u}; uint8_t success_time = 0; uint32_t cs_offset = 0; int32_t ret = SDRV_STATUS_FAIL; if (gate_val != NULL) { SDRAMC_CTL_DATA_DEBUG("gate = 0x%08x\r\n", gate_val[ddr_gate_index][0]); } struct sd_para *sd_dll_bt = sdrv_get_sd_para(); cs_offset = cs_num << sd_dll_bt->cs_start; for (uint32_t i = 0; i < TRAINING_STEPS; i++) { p_pattern = (sdram_data_t *)training_patten; p_data = (sdram_data_t *)read_data; ret = sdramc_dll_config_all(base, i); if(ret != SDRV_STATUS_OK){ goto err; } /* write test pattern to sdram address */ for (uint32_t j = 0; j < SDRAMC_PATTERN_LENS; j++) { training_write(*(p_pattern + j), (uint8_t *)CONFIG_SDRAM_BASE + cs_offset + SDRAMC_WIDTH * j); } arch_clean_invalidate_cache_range((addr_t)CONFIG_SDRAM_BASE + cs_offset, SDRAMC_WIDTH * SDRAMC_PATTERN_LENS); //DSB; /* read sdram address data to read_data array */ for (uint32_t j = 0; j < SDRAMC_PATTERN_LENS; j++) { *(p_data + j) = training_read((uint8_t *)CONFIG_SDRAM_BASE + cs_offset + SDRAMC_WIDTH * j); } /* Matrix reverse */ sdrv_matrix_transpose(read_data, training_data[i], SDRAMC_PATTERN_LENS, SDRAMC_WIDTH); /* compare training_data and training_patten_trans data */ for (uint32_t j = 0; j < SDRAMC_WIDTH; j++) { for (uint32_t k = 0; k < SDRAMC_PATTERN_LENS; k += SDRAMC_WIDTH) { data_cmp_res = *((sdram_data_t *)training_data[i] + j + k) == *((sdram_data_t *)training_patten_trans + j + k); if (data_cmp_res == false) break; } /* check success and record maximal left window and minimal right window */ if (data_cmp_res) { if (training_succ[j] == false) { training_succ[j] = true; pass_win_l[j] = data_cmp_res ? i : 0u; save_pass_win_l[j] = pass_win_l[j]; SDRAMC_CTL_DATA_DEBUG("sdramc dll%d training succ! win_l is %d\r\n", j, pass_win_l[j]); } } else { if (training_succ[j] == true && (i > (save_pass_win_l[j] + 15u))) { training_succ[j] = true; } else { training_succ[j] = false; } } if (training_succ[j] == true && (!data_cmp_res || i == 31u) && training_end[j] != true) { training_end[j] = true; pass_win_r[j] = data_cmp_res ? 31u : (i - 1u); SDRAMC_CTL_DATA_DEBUG("sdramc dll%d training succ! win_r is %d\r\n", j, pass_win_r[j]); pass_win_min_l2r = MIN(pass_win_r[j] - pass_win_l[j] + 1u, pass_win_min_l2r); sdramc_getwin(ddr_mode, ddr_gate_index, gate_val, training_win_data_ddr, cs_num, pass_win_min_l2r, pass_win_l[j], pass_win_r[j], j); SDRAMC_CTL_DATA_DEBUG("\n dll%d, l = %d , r = %d, mid = %d \r\n", j, pass_win_l[j], pass_win_r[j], (pass_win_r[j] - pass_win_l[j]) / 2 + pass_win_l[j]); /* success and sdramc config dll */ ret = sdramc_dll_config(base, (pass_win_r[j] - pass_win_l[j]) / 2u + pass_win_l[j], j); if(ret != SDRV_STATUS_OK){ goto err; } success_time++; } } if (success_time == SDRAMC_WIDTH) { SDRAMC_CTL_DATA_DEBUG("dll training success times = %d \r\n", success_time); break; } } if (success_time != SDRAMC_WIDTH) { //clear failed gate_index if (ddr_mode == true) { gate_val[ddr_gate_index][1] = 0; } SDRAMC_CTL_DATA_DEBUG("%d way dll failed \r\n", SDRAMC_WIDTH - success_time); ret = SDRV_STATUS_SDRAMC_DLL_WAYS_ERR; } sdrv_training_result_dump(pass_win_min_l2r, success_time); err: return ret; } /** * @brief sdramc sdr training * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @param [in] training_win_data set sdr training result * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_sdr_training_data(addr_t base, sdram_config_t *conf, uint32_t (*training_win_data)[2][4][2]) { int32_t ret = SDRV_STATUS_FAIL; for (uint32_t sdr_cs_num = 0; sdr_cs_num < conf->cs_num; sdr_cs_num++) { ret = sdramc_dll_training(base, 0u, NULL, false, training_win_data, sdr_cs_num); if(ret != SDRV_STATUS_OK){ return ret; } } dump_training_win_result(training_win_data, conf, 1, false); return ret; } /** * @brief sdramc sdr training * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @param [in] training_win get value and set sdr dll mid value * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_sdr_set_dll_config(addr_t base, sdram_config_t *conf, uint32_t (*training_win)[2][4][2]) { int32_t ret = SDRV_STATUS_FAIL; uint32_t dll_l_r[4][2] = {{0u, 33u}, {0u, 33u}, {0u, 33u}, {0u, 33u}}; for (uint32_t dll_index = 0; dll_index < conf->dq_width / 8u; dll_index++) { for (uint32_t cs_num = 0; cs_num < conf->cs_num; cs_num++) { dll_l_r[dll_index][0] = MAX(training_win[cs_num][0][dll_index][0], dll_l_r[dll_index][0]); dll_l_r[dll_index][1] = MIN(training_win[cs_num][0][dll_index][1], dll_l_r[dll_index][1]); } } for (uint32_t i = 0; i < conf->dq_width / 8u; i++) { uint32_t dll_mid = (dll_l_r[i][1] - dll_l_r[i][0]) / 2 + dll_l_r[i][0]; ret = sdramc_dll_config(base, dll_mid, i); if(ret != SDRV_STATUS_OK){ return ret; } } return ret; } /** * @brief gate set value for ddr mode * @param [in] cas_num use cas latency value and set DQS_SHIFT register * @param [in] index gate array index * @return uint32_t */ static uint32_t sdrv_gate_set_val(uint32_t cas_num, uint32_t index) { uint32_t gate_reg = 0; gate_reg = cas_num << SDRAMC_DDR_DQS_SHIFT_NUM0_LSB; gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM1_LSB; gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM2_LSB; gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM3_LSB; if (index == 1) { gate_reg |= (cas_num + 1u) << SDRAMC_DDR_DQS_SHIFT_NUM0NEG_LSB; gate_reg |= (cas_num + 1u) << SDRAMC_DDR_DQS_SHIFT_NUM1NEG_LSB; gate_reg |= (cas_num + 1u) << SDRAMC_DDR_DQS_SHIFT_NUM2NEG_LSB; gate_reg |= (cas_num + 1u) << SDRAMC_DDR_DQS_SHIFT_NUM3NEG_LSB; } else { gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM0NEG_LSB; gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM1NEG_LSB; gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM2NEG_LSB; gate_reg |= cas_num << SDRAMC_DDR_DQS_SHIFT_NUM3NEG_LSB; } return gate_reg; } /** * @brief sdramc ddr set gate value, and this is * the reference value according to the frequency * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @param [in] gate_val gate value for ddr mode * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_ddr_set_gate(addr_t base, sdram_config_t *conf, uint32_t (*gate_val)[2]) { uint32_t val = conf->clock / 1000000u; SDRAMC_CTL_DATA_DEBUG("cas = %d, val = %d\r\n", conf->cas_latency_x2, val); int32_t ret = SDRV_STATUS_FAIL; if (val >= 366u && val < 410u) { gate_val[0][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 0); gate_val[1][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 1); ret = SDRV_STATUS_OK; } else if (val >= 333u && val < 366u) { gate_val[0][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 0); gate_val[1][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 1); ret = SDRV_STATUS_OK; } else if (val >= 300u && val < 333u) { gate_val[0][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 3u, 1); gate_val[1][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 0); ret = SDRV_STATUS_OK; } else if (val >= 266u && val < 300u) { gate_val[0][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 3u, 1); gate_val[1][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 0); ret = SDRV_STATUS_OK; } else if (val >= 233u && val < 266u) { gate_val[0][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 3u, 1); gate_val[1][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 2u, 0); ret = SDRV_STATUS_OK; } else if (val >= 190u && val < 233u) { gate_val[0][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 3u, 0); gate_val[1][0] = sdrv_gate_set_val(conf->cas_latency_x2 - 3u, 1); ret = SDRV_STATUS_OK; } else { ssdk_printf(SSDK_CRIT, "conf->clock set failed \r\n"); } return ret; } /** * @brief sdramc ddr training data * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @param [in] gate_times twice ddr mode * @param [in] gate_val record gate value * @param [in] gate_index gate index0 or index1 * @param [in] training_win record training result * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_ddr_training_data(addr_t base, sdram_config_t *conf, uint32_t gate_times, uint32_t (*gate_val)[2], const uint32_t gate_index, uint32_t (*training_win)[2][4][2]) { int32_t ret = SDRV_STATUS_FAIL; for (uint32_t ddr_cs_num = 0; ddr_cs_num < conf->cs_num; ddr_cs_num++) { ddr_writel(gate_val[gate_index][0], base + SDRAMC_DDR_DQS_SHIFT); ret = sdramc_dll_training(base, gate_index, gate_val, true, training_win, ddr_cs_num); } dump_training_win_result(training_win, conf, gate_times, true); return ret; } /** * @brief sdramc ddr dll config * This function use get key and get value * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @param [in] gate_val ddr gate value * @param [in] gate_nums gate index * @return gate_record_s */ static gate_record_s sdramc_ddr_dll_config(addr_t base, sdram_config_t *conf, uint32_t (*gate_val)[2], uint32_t gate_nums) { uint32_t max_val = 0u; uint32_t get_key = 0u; uint32_t gate_index; gate_record_s record; for (uint32_t times = 0u; times < gate_nums; times++) { if (gate_val[times][1] < WIN_MAX_SUCCESS) max_val = MAX(gate_val[times][1], max_val); } SDRAMC_CTL_DATA_DEBUG("max_success_times = %d \r\n", max_val); for (gate_index = 0; gate_index < gate_nums; gate_index++) { if (max_val == gate_val[gate_index][1]) { get_key = gate_val[gate_index][0]; break; } } SDRAMC_CTL_DATA_DEBUG("get_key = 0x%08x \r\n", get_key); record.gate_val = get_key; record.gate_index = gate_index; return record; } /** * @brief sdramc ddr set dll config * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @param [in] training_win_data_ddr record sdr or ddr result * @param [in] gate_s ddr gate array value * @return int32_t * @retval 0: success * @retval other: failed */ static int32_t sdramc_ddr_set_dll_config(addr_t base, sdram_config_t *conf, uint32_t (*training_win_data_ddr)[2][4][2], const gate_record_s *gate_s) { int32_t ret = SDRV_STATUS_FAIL;; uint32_t dll_l_r[4][2] = {{0u, 33u}, {0u, 33u}, {0u, 33u}, {0u, 33u}}; uint32_t dll_mid = 0; ddr_writel(gate_s->gate_val, base + SDRAMC_DDR_DQS_SHIFT); for (uint32_t dll_index = 0; dll_index < conf->dq_width / 8u; dll_index++) { for (uint32_t cs_num = 0; cs_num < conf->cs_num; cs_num++) { dll_l_r[dll_index][0] = MAX( training_win_data_ddr[cs_num][gate_s->gate_index][dll_index][0], dll_l_r[dll_index][0]); dll_l_r[dll_index][1] = MIN( training_win_data_ddr[cs_num][gate_s->gate_index][dll_index][1], dll_l_r[dll_index][1]); } } training_maxl2minr_dump(dll_l_r); for (uint32_t i = 0; i < conf->dq_width / 8u; i++) { if (dll_l_r[i][1] > dll_l_r[i][0]) { dll_mid = (dll_l_r[i][1] - dll_l_r[i][0]) / 2 + dll_l_r[i][0]; } else { return ret; } /* find dll mid value and config */ ret = sdramc_dll_config(base, dll_mid, i); if (ret != SDRV_STATUS_OK) { return ret; } } return ret; } /** * @brief sdramc init main function * * 1.config sdr/ddr register according to different mode * 2.training and config dll value for sdr/ddr mode * * @param [in] base sdramc base register address * @param [in] conf sdramc config parameters * @return int32_t * @retval 0: success * @retval other: failed */ int32_t sdrv_sdramc_init(addr_t base, sdram_config_t *conf) { uint32_t reg = 0u; uint32_t data_width_mode; int32_t ret = SDRV_STATUS_FAIL; if (conf->ddr_mode) { reg = SDRAMC_INIT_DDR_ASYNC_FLUSH_B; } ddr_writel(SDRAMC_INIT_SOFT_RESET, base + SDRAMC_INIT); ddr_writel(reg, base + SDRAMC_INIT); ddr_readl(base + SDRAMC_INIT); ddr_writel((uint32_t)0xFFFFFFFFu, base + SDRAMC_AXI_RDY_CNT); //reg = conf->ddr_mode == false ? 3u : 0u; /* wrdly clk or ddrdqs */ reg = conf->ddr_mode == false ? 1u : 0u; /* lpb clk or ddrdqs */ ddr_writel(reg, base + RD_CLK); reg = conf->ddr_mode == false ? 1u : 0u; ddr_writel(reg, base + SDRAM_DDR_MODE); ddr_writel(log2_uint(conf->burst_length), base + SDRAM_BURST_MODE); switch (conf->dq_width) { case 32u: data_width_mode = 0u; break; case 16u: data_width_mode = 1u; break; case 8u: data_width_mode = 2u; break; default: return -1; } ddr_writel(data_width_mode, base + SDRAM_WIDTH_MODE); if (conf->cas_latency_x2 == 5u) { /* 2.5 cas latency value is 6 */ reg = 6u << SDRAMC_CAS_MODE_LSB; } else { reg = conf->cas_latency_x2 / 2u << SDRAMC_CAS_MODE_LSB; } if (conf->ddr_mode) { reg |= (conf->cas_latency_x2 - 2u + 4u) << SDRAMC_CAS_MODE_RD_NONE_DLY; /* default val + 4 */ } else { reg |= (conf->cas_latency_x2 / 6u + 4u) << SDRAMC_CAS_MODE_RD_NONE_DLY; } ddr_writel(reg, base + SDRAMC_CAS_MODE); reg = 0x0Fu << SDRAMC_SCH_WEIGHT_QOS_W_LSB; reg |= 0x02u << SDRAMC_SCH_WEIGHT_BANKIDLE_W_LSB; reg |= 0x10u << SDRAMC_SCH_WEIGHT_PAGE_W_LSB; reg |= 0x01u << SDRAMC_SCH_WEIGHT_RW_W_LSB; ddr_writel(reg, base + SDRAMC_SCH_WEIGHT); reg = 0x02u << SDRAMC_AGING_WEIGHT_LSB; ddr_writel(reg, base + SDRAMC_AGING_WEIGHT); // TODO: if (conf->ddr_mode == false) { ddr_writel((uint32_t)0u, base + SDRAMC_OPERATING_MODE); } else { // TODO: just for test cas latency ddr_writel((uint32_t)0u, base + SDRAMC_EXTEND_CONTROL); /* enable dll */ ddr_writel((uint32_t)0u, base + SDRAMC_OPERATING_MODE); } reg = SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_ras_ns) << SDRAMC_TIMING_TRAS_MIN_LSB; reg |= SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_rc_ns) << SDRAMC_TIMING_TRC_LSB; reg |= SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_rfc_ns) << SDRAMC_TIMING_TRFC_LSB; reg |= SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_rcd_ns) << SDRAMC_TIMING_TRCD_LSB; SDRAMC_CTL_DATA_DEBUG("PART1: 0x%x\r\n", reg); ddr_writel(reg, base + SDRAMC_TIMING_REGS_PART1); reg = SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_rp_ns) << SDRAMC_TIMING_TRP_LSB; reg |= SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_wr_ns) << SDRAMC_TIMING_TWR_LSB; reg |= SDRAMC_NS_2_TICKS(conf->clock, conf->comm_config->t_rrd_ns) << SDRAMC_TIMING_TRRD_LSB; reg |= conf->comm_config->t_wtr_cycle << SDRAMC_TIMING_TWTR_LSB; SDRAMC_CTL_DATA_DEBUG("PART2: 0x%x\r\n", reg); ddr_writel(reg, base + SDRAMC_TIMING_REGS_PART2); // TODO: reg = (64u * (conf->clock / 1000u)) / conf->row_num - 60u; reg |= (conf->ddr_mode == false ? 200u : 400u) << SDRAMC_TIMING_CNT_200CYCLE_LSB; SDRAMC_CTL_DATA_DEBUG("PART3: 0x%x\r\n", reg); ddr_writel(reg, base + SDRAMC_TIMING_REGS_PART3); /* set init 200us count */ reg = 200u * (conf->clock / 1000000u); ddr_writel(reg, base + SDRAMC_INIT_WAIT_CONT); /* ||<--row-->|<---cs--->|<--bank-->|<--column-->|<--dq-width-->|| */ struct sd_para *sd_bt = sdrv_get_sd_para(); sd_bt->column_start = 8u; SDRAMC_CTL_DATA_DEBUG("column_start = %d\r\n", sd_bt->column_start); sd_bt->column_bits = log2_uint(conf->column_num); SDRAMC_CTL_DATA_DEBUG("column_bits = %d\r\n", sd_bt->column_bits); reg = sd_bt->column_start << SDRAMC_ADDR_DIVED_MODE_COLOW_START_LSB; sd_bt->bank_start = sd_bt->column_bits + log2_uint(conf->dq_width) - 3u; SDRAMC_CTL_DATA_DEBUG("bank_start = %d\r\n", sd_bt->bank_start); sd_bt->bank_bits = log2_uint(conf->bank_num); SDRAMC_CTL_DATA_DEBUG("bank_bits = %d\r\n", sd_bt->bank_bits); reg |= sd_bt->bank_start << SDRAMC_ADDR_DIVED_MODE_BANK_START_LSB; sd_bt->cs_start = sd_bt->bank_start + sd_bt->bank_bits;; SDRAMC_CTL_DATA_DEBUG("cs_start = %d\r\n", sd_bt->cs_start); sd_bt->cs_bits = log2_uint(conf->cs_num); SDRAMC_CTL_DATA_DEBUG("cs_bits = %d\r\n", sd_bt->cs_bits); reg |= sd_bt->cs_start << SDRAMC_ADDR_DIVED_MODE_CS_START_LSB; sd_bt->row_start = sd_bt->cs_start + sd_bt->cs_bits; SDRAMC_CTL_DATA_DEBUG("row_start = %d\r\n", sd_bt->row_start); sd_bt->row_bits = log2_uint(conf->row_num); SDRAMC_CTL_DATA_DEBUG("row_bits = %d\r\n", sd_bt->row_bits); reg |= sd_bt->row_start << SDRAMC_ADDR_DIVED_MODE_ROW_START_LSB; reg |= (sd_bt->row_bits - 7u) << SDRAMC_ADDR_DIVED_MODE_ROW_MODE_LSB; ddr_writel(reg, base + SDRAMC_ADDR_DIVED_MODE); reg = sd_bt->cs_bits << SDRAMC_CHIP_SELECT_MODE_LSB; reg |= (sd_bt->column_bits - 5u) << SDRAMC_CHIP_SELECT_COLOW_MODE_LSB; ddr_writel(reg, base + SDRAMC_CHIP_SELECT_MODE); ddr_writel(sd_bt->bank_bits, base + SDRAMC_BANK_MODE); sdrv_set_sd_para(sd_bt); /* save config info */ // TODO: reg = (conf->ddr_mode == false ? 200u : 400u) << SDRAMC_TIMING_TXSRD_LSB; reg |= SDRAMC_NS_2_TICKS(conf->clock, 70000u) - 50u; ddr_writel(reg, base + SDRAMC_TIMING_REGS_PART4); reg = (conf->ddr_mode == false ? 1u : 2u) * conf->comm_config->mrd_cycle; reg = reg << SDRAMC_MRD_TMR_LSB; ddr_writel(reg, base + SDRAMC_MRD); reg = 0u; if (conf->ddr_mode) { reg = SDRAMC_INIT_DDR_ASYNC_FLUSH_B; ddr_writel(reg, base + SDRAMC_INIT); } ddr_writel((uint32_t)0u, base + SDRAMC_INIT); if (conf->ddr_mode) { reg = SDRAMC_INIT_DDR_ASYNC_FLUSH_B; ddr_writel(reg, base + SDRAMC_INIT); } /* Enable clock out */ reg |= SDRAMC_INIT_DDR_ASYNC_FLUSH_B; reg |= SDRAMC_INIT_CLK_OUT_EN; ddr_writel(reg, base + SDRAMC_INIT); // TODO: sdramc enable reg |= SDRAMC_INIT_START; ddr_writel(reg, base + SDRAMC_INIT); /* wait for sdramc register config end */ while (1) { reg = ddr_readl(base + SDRAMC_INIT); if (reg & SDRAMC_INIT_DONE) { ssdk_printf(SSDK_CRIT, "sdramc reg config init done!\r\n"); break; } } reg = ddr_readl(base + SDRAMC_FLOW_CTRL); reg &= ~(uint32_t)(SDRAMC_FLOW_CTRL_WR_SET | SDRAMC_FLOW_CTRL_AW_SET | SDRAMC_FLOW_CTRL_AR_SET); ddr_writel(reg, base + SDRAMC_FLOW_CTRL); /* cs gate(ddr) dllx win_lr */ uint32_t training_win_record[4u][2u][4u][2u] = {0u}; if (conf->ddr_mode == true) { uint32_t gate_val[][2u] = { {0u, WIN_MAX_SUCCESS}, {0u, WIN_MAX_SUCCESS}, }; /* set the gate value depending on the frequency */ ret = sdramc_ddr_set_gate(base, conf, gate_val); if (ret != SDRV_STATUS_OK) { ssdk_printf(SSDK_CRIT, "ddr set gate failed!\r\n"); goto exit; } for (uint32_t gate_index = 0; gate_index < ARRAY_SIZE(gate_val); gate_index++) { /* entry ddr mode training, and choose the best result from gate value*/ ret = sdramc_ddr_training_data(base, conf, ARRAY_SIZE(gate_val), gate_val, gate_index, training_win_record); if (ret != SDRV_STATUS_OK) { ssdk_printf(SSDK_CRIT, "ddr training data failed!\r\n"); goto exit; } } /* ddr get gate val and index */ gate_record_s gate_s = sdramc_ddr_dll_config(base, conf, gate_val, ARRAY_SIZE(gate_val)); SDRAMC_CTL_DATA_DEBUG("gate_val = 0x%x, gate_index = %d \r\n", gate_s.gate_val, gate_s.gate_index); SDRAMC_CTL_DATA_DEBUG("%d , %d\r\n", gate_val[0][1], gate_val[1][1]); /* ddr set save record result for dll */ ret = sdramc_ddr_set_dll_config(base, conf, training_win_record, &gate_s); if (ret != SDRV_STATUS_OK) { ssdk_printf(SSDK_CRIT, "ddr set dll config failed!\r\n"); goto exit; } } else { ddr_writel(SDRAMC_DQS_QE_SET_VAL | SDRAMC_DQS_QE_SET_EN, base + SDRAMC_DQS_QE_SET); uint32_t cas_num = conf->cas_latency_x2 / 2u - 1u; ddr_writel(cas_num, base + SDRAMC_SDR_DQS_SHIFT); /* entry sdr mode training */ ret = sdramc_sdr_training_data(base, conf, training_win_record); if (ret != SDRV_STATUS_OK) { ssdk_printf(SSDK_CRIT, "sdr training data failed!\r\n"); goto exit; } /* sdr training end and set dll */ ret = sdramc_sdr_set_dll_config(base, conf, training_win_record); if (ret != SDRV_STATUS_OK) { ssdk_printf(SSDK_CRIT, "sdr dll config failed!\r\n"); goto exit; } } exit: return ret; }