Files
E3_boot/drivers/source/sdramc/sdrv_sdramc.c
2025-11-07 10:05:24 +08:00

1028 lines
35 KiB
C

/**
* @file sdrv_sdramc.c
* @brief sdrv sdramc driver
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#include <armv7-r/cache.h>
#include <param.h>
#include <types.h>
#include <sdrv_sdramc.h>
#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;
}