ddr: marvell: update DDR driver
v2 cover ddr: marvell: update DDR driver
I have a custom board that sees correctable ECC errors (when running memtester[1] from Linux). When I used Marvell's bootloader I didn't see the errors.
I've also had a tame hardware engineer (if there is such a thing) looking at the timing waveforms comparing the stock u-boot behaviour against Marvell's bootloader.
The changes for this series have been derived by comparing scope captures and register output between the same system running stock u-boot and Marvell's USP.
Changes in v2:
- Update tODT_OFF_WR as well
- Added patches 2-4
Chris Packham (4):
ddr: marvell: only assert M_ODT[0] on write for a single CS
ddr: marvell: use correct TREFI value
ddr: marvell: update additional ODT setting
ddr: marvell: update ddr controller init and freq
drivers/ddr/marvell/a38x/ddr3_init.h | 3 +- drivers/ddr/marvell/a38x/ddr3_topology_def.h | 3 +- drivers/ddr/marvell/a38x/ddr3_training.c | 50 +++++++++++++++--------- drivers/ddr/marvell/a38x/ddr3_training_db.c | 19 +++++---- drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c | 22 +++++++---- drivers/ddr/marvell/a38x/ddr3_training_static.c | 3 +- 6 files changed, 62 insertions(+), 38 deletions(-)
Signature · 1 line
2.15.1
PATCH v2 1/4 ddr: marvell: only assert M_ODT[0] on write for a single CS
When using only a single DDR chip select only assert M_ODT[0] on write. Do not assert it on read and do not assert M_ODT[1] at all. Also set tODT_OFF_WR to 0x9 which contradicts the recommendation from the functional spec but is what Marvell's binary training blob does and seems to give better results when ODT is active during writes.
---
Changes in v2:
- Update tODT_OFF_WR as well
drivers/ddr/marvell/a38x/ddr3_init.h | 3 ++- drivers/ddr/marvell/a38x/ddr3_training.c | 15 +++++++++------ drivers/ddr/marvell/a38x/ddr3_training_static.c | 3 ++- 3 files changed, 13 insertions(+), 8 deletions(-)
drivers/ddr/marvell/a38x/ddr3_init.h +2 -1
@@ -183,7 +183,8 @@ extern u32 g_znodt_data;extern u32 g_zpodt_ctrl;extern u32 g_znodt_ctrl;extern u32 g_dic; -extern u32 g_odt_config; +extern u32 g_odt_config_2cs; +extern u32 g_odt_config_1cs;extern u32 g_rtt_nom;extern u8 debug_training_access;
drivers/ddr/marvell/a38x/ddr3_training.c +9 -6
@@ -315,6 +315,7 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_enum hws_access_type access_type =ACCESS_TYPE_UNICAST ;u32 data_read[MAX_INTERFACE_NUM ];struct hws_topology_map *tm =ddr3_get_topology_map (); +u32 odt_config = g_odt_config_2cs; DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, ("Init_controller, do_mrs_phy=%d, is_ctrl64_bit=%d\n",
@@ -570,6 +571,9 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_DUNIT_CONTROL_HIGH_REG , (init_cntr_prm->msys_init << 7), (1 << 7))); +/* calculate number of CS (per interface) */ +CHECK_STATUS (calc_cs_num + (dev_num, if_id, &cs_num)); timing = tm->interface_params[if_id].timing;if (mode2_t !=0xff ) {
@@ -578,9 +582,6 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_/* Board topology map is forcing timing */ t2t = (timing ==HWS_TIM_2T ) ?1 :0 ; }else { -/* calculate number of CS (per interface) */ -CHECK_STATUS (calc_cs_num - (dev_num, if_id, &cs_num)); t2t = (cs_num ==1 ) ?0 :1 ; }
@@ -623,9 +624,11 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_ (1 << 11)));/* Set Active control for ODT write transactions */ +if (cs_num ==1 ) + odt_config = g_odt_config_1cs;CHECK_STATUS (ddr3_tip_if_write (dev_num,ACCESS_TYPE_MULTICAST , -PARAM_NOT_CARE ,0x1494 , g_odt_config, +PARAM_NOT_CARE ,0x1494 , odt_config,MASK_ALL_BITS )); } }else {
@@ -1539,7 +1542,7 @@ int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type access_type,CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,ODT_TIMING_LOW , val,0xffff0 )); - val =0x71 | ((cwl_value -1 ) <<8 ) | ((cwl_value +5 ) <<12 ); + val =0x91 | ((cwl_value -1 ) <<8 ) | ((cwl_value +5 ) <<12 );CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,ODT_TIMING_HI_REG , val,0xffff ));
@@ -1591,7 +1594,7 @@ static int ddr3_tip_write_odt(u32 dev_num, enum hws_access_type access_type,CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,ODT_TIMING_LOW , val,0xffff0 )); - val =0x71 | ((cwl_value -1 ) <<8 ) | ((cwl_value +5 ) <<12 ); + val =0x91 | ((cwl_value -1 ) <<8 ) | ((cwl_value +5 ) <<12 );CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,ODT_TIMING_HI_REG , val,0xffff ));if (odt_additional ==1 ) {
drivers/ddr/marvell/a38x/ddr3_training_static.c +2 -1
@@ -21,7 +21,8 @@ u32 g_zpodt_data = 45; /* controller data - P ODT */u32 g_znodt_data =45 ;/* controller data - N ODT */ u32 g_zpodt_ctrl =45 ;/* controller data - P ODT */ u32 g_znodt_ctrl =45 ;/* controller data - N ODT */ -u32 g_odt_config =0x120012 ; +u32 g_odt_config_2cs =0x120012 ; +u32 g_odt_config_1cs =0x10000 ;u32 g_rtt_nom =0x44 ;u32 g_dic =0x2 ;
Signature · 1 line
2.15.1
PATCH v2 2/4 ddr: marvell: use correct TREFI value
In reply to Chris PackhamThe ternary operation had the HIGH/LOW values the wrong way round. Update it to use the correct value.
---
Changes in v2:
- new
drivers/ddr/marvell/a38x/ddr3_training.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ddr/marvell/a38x/ddr3_training.c +1 -1
@@ -1375,7 +1375,7 @@ int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type access_type,/* adjust t_refi to new frequency */ t_refi = (tm->interface_params[if_id].interface_temp == -HWS_TEMP_HIGH ) ?TREFI_LOW :TREFI_HIGH ; +HWS_TEMP_HIGH ) ?TREFI_HIGH :TREFI_LOW ; t_refi *=1000 ;/*psec */ /* HCLK in[ps] */
Signature · 1 line
2.15.1
PATCH v2 3/4 ddr: marvell: update additional ODT setting
In reply to Chris PackhamThe RD_SAMPLE_DELAY field is 5 bits so it needs to be masked with 0x1f instead of 0xf. Rather than checking the read sample delay for all DDR chip selects use the values for the chip selects that are actually configured. Finally continue searching for the max_phase value even if the current read_sample is the same as the max_read_sample.
---
Changes in v2:
- new
drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c +14 -8
@@ -17,7 +17,7 @@#define VREF_MAX_INDEX 7#define MAX_VALUE (1024 - 1)#define MIN_VALUE (-MAX_VALUE) -#define GET_RD_SAMPLE_DELAY (data, cs) ((data >> rd_sample_mask[cs]) & 0xf) +#define GET_RD_SAMPLE_DELAY (data, cs) ((data >> rd_sample_mask[cs]) & 0x1f)u32 ck_delay = (u32)-1 , ck_delay_16 = (u32)-1 ;u32 ca_delay;
@@ -49,7 +49,7 @@ static u32 rd_sample_mask[] = {*/ int ddr3_tip_write_additional_odt_setting (u32 dev_num,u32 if_id) { -u32 cs_num =0 , max_read_sample =0 , min_read_sample =0 ; +u32 cs_num =0 , max_cs =0 , max_read_sample =0 , min_read_sample =0x1f ;u32 data_read[MAX_INTERFACE_NUM ] = {0 };u32 read_sample[MAX_CS_NUM ];u32 val;
@@ -66,15 +66,19 @@ int ddr3_tip_write_additional_odt_setting(u32 dev_num, u32 if_id) data_read, MASK_ALL_BITS)); val = data_read[if_id]; -for (cs_num =0 ; cs_num <MAX_CS_NUM ; cs_num++) { + max_cs =hws_ddr3_tip_max_cs_get (); + +for (cs_num =0 ; cs_num < max_cs; cs_num++) { read_sample[cs_num] =GET_RD_SAMPLE_DELAY (val, cs_num);/* find maximum of read_samples */ if (read_sample[cs_num] >= max_read_sample) { -if (read_sample[cs_num] == max_read_sample) - max_phase =MIN_VALUE ; -else +if (read_sample[cs_num] == max_read_sample) { +/* search for max phase */ ; + }else { max_read_sample = read_sample[cs_num]; + max_phase =MIN_VALUE ; + } for (pup_index = 0; pup_index < tm->num_of_bus_per_interface;
@@ -97,10 +101,12 @@ int ddr3_tip_write_additional_odt_setting(u32 dev_num, u32 if_id) min_read_sample = read_sample[cs_num]; } +if (min_read_sample <= tm->interface_params[if_id].cas_l) { + min_read_sample = (int )tm->interface_params[if_id].cas_l; + } + min_read_sample = min_read_sample -1 ; max_read_sample = max_read_sample +4 + (max_phase +1 ) /2 +1 ; -if (min_read_sample >=0xf ) - min_read_sample =0xf ;if (max_read_sample >=0x1f ) max_read_sample =0x1f ;
Signature · 1 line
2.15.1
PATCH v2 4/4 ddr: marvell: update ddr controller init and freq
In reply to Chris PackhamUpdate the calculation for tWR and tPD. This improves the DDR refresh interval and brings the initialization into line with the binary blobs currently being supplied by Marvell.
---
Changes in v2:
- new
drivers/ddr/marvell/a38x/ddr3_topology_def.h | 3 ++- drivers/ddr/marvell/a38x/ddr3_training.c | 33 ++++++++++++++++++---------- drivers/ddr/marvell/a38x/ddr3_training_db.c | 19 +++++++++------- 3 files changed, 34 insertions(+), 21 deletions(-)
drivers/ddr/marvell/a38x/ddr3_topology_def.h +2 -1
@@ -70,7 +70,8 @@ enum speed_bin_table_elements {SPEED_BIN_TWTR ,SPEED_BIN_TRTP ,SPEED_BIN_TWR , -SPEED_BIN_TMOD +SPEED_BIN_TMOD , +SPEED_BIN_TXPDLL };#endif /* _DDR3_TOPOLOGY_DEF_H */
drivers/ddr/marvell/a38x/ddr3_training.c +21 -12
@@ -22,6 +22,8 @@#define GET_CS_FROM_MASK (mask) (cs_mask2_num[mask])#define CS_CBE_VALUE (cs_num) (cs_cbe_reg[cs_num]) +#define TIMES_9_TREFI_CYCLES 0x8 +u32 window_mem_addr =0 ;u32 phy_reg0_val =0 ;u32 phy_reg1_val =8 ;
@@ -508,7 +510,9 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_DEBUG_TRAINING_IP (DEBUG_LEVEL_TRACE , ("cl_value 0x%x cwl_val 0x%x\n" , cl_value, cwl_val)); - + t_wr =TIME_2_CLOCK_CYCLES (speed_bin_table (speed_bin_index, +SPEED_BIN_TWR ), + t_ckclk); data_value = ((cl_mask_table[cl_value] &0x1 ) <<2 ) | ((cl_mask_table[cl_value] &0xe ) <<3 );
@@ -518,8 +522,9 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_ (0x7 << 4) | (1 << 2)));CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id, -MR0_REG , twr_mask_table[t_wr +1 ], -0xe00 )); +MR0_REG , twr_mask_table[t_wr +1 ] <<9 , + (0x7 <<9 ))); +/* * MR1: Set RTT and DIC Design GL values
@@ -590,16 +595,15 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_DDR_CONTROL_LOW_REG , t2t <<3 , 0x3 << 3));/* move the block to ddr3_tip_set_timing - start */ - t_pd =GET_MAX_VALUE (t_ckclk *3 , -speed_bin_table (speed_bin_index, -SPEED_BIN_TPD )); - t_pd =TIME_2_CLOCK_CYCLES (t_pd, t_ckclk); - txpdll =GET_MAX_VALUE (t_ckclk *10 ,24 ); + t_pd =TIMES_9_TREFI_CYCLES ; + txpdll =GET_MAX_VALUE (t_ckclk *10 , +speed_bin_table (speed_bin_index, +SPEED_BIN_TXPDLL )); txpdll =CEIL_DIVIDE ((txpdll -1 ), t_ckclk);CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id, -DDR_TIMING_REG , txpdll <<4 , -0x1f <<4 )); +DDR_TIMING_REG , txpdll <<4 | t_pd, +0x1f <<4 |0xf ));CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,DDR_TIMING_REG ,0x28 <<9 ,0x3f <<9 ));
@@ -1227,6 +1231,7 @@ int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type access_type,u32 cl_value =0 , cwl_value =0 , mem_mask =0 , val =0 , bus_cnt =0 , t_hclk =0 , t_wr =0 , refresh_interval_cnt =0 , cnt_id; +u32 t_ckclk;u32 t_refi =0 , end_if, start_if;u32 bus_index =0 ;int is_dll_off =0 ;
@@ -1393,8 +1398,12 @@ int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type access_type,CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,DFS_REG , (cwl_mask_table[cwl_value] <<12 ),0x7000 )); - t_wr =speed_bin_table (speed_bin_index,SPEED_BIN_TWR ); - t_wr = (t_wr /1000 ); + + t_ckclk =MEGA / freq_val[frequency]; + t_wr =TIME_2_CLOCK_CYCLES (speed_bin_table (speed_bin_index, +SPEED_BIN_TWR ), + t_ckclk); +CHECK_STATUS (ddr3_tip_if_write (dev_num, access_type, if_id,DFS_REG , (twr_mask_table[t_wr +1 ] <<16 ),0x70000 ));
drivers/ddr/marvell/a38x/ddr3_training_db.c +11 -8
@@ -152,18 +152,18 @@ u8 twr_mask_table[] = {10 ,10 ,10 , -1 ,/*5 */ -2 ,/*6 */ -3 ,/*7 */ +1 ,/*5*/ +2 ,/*6*/ +3 ,/*7*/ +4 ,/*8*/ 10 , +5 ,/*10*/ 10 , -5 ,/*10 */ +6 ,/*12*/ 10 , -6 ,/*12 */ +7 ,/*14*/ 10 , -7 ,/*14 */ -10 , -0 /*16 */ +0 /*16*/ }; u8 cl_mask_table[] = {
@@ -431,6 +431,9 @@ u32 speed_bin_table(u8 index, enum speed_bin_table_elements element)case SPEED_BIN_TMOD : result =15000 ;break ; +case SPEED_BIN_TXPDLL : + result =24000 ; +break ;default :break ; }
Signature · 1 line
2.15.1
v2 cover ddr: marvell: update DDR driver
In reply to Chris PackhamHi Chris,
Quoted 22 lines · Chris Packham
I have a custom board that sees correctable ECC errors (when running memtester[1] from Linux). When I used Marvell's bootloader I didn't see the errors.
I've also had a tame hardware engineer (if there is such a thing) looking at the timing waveforms comparing the stock u-boot behaviour against Marvell's bootloader.
The changes for this series have been derived by comparing scope captures and register output between the same system running stock u-boot and Marvell's USP.
Changes in v2:
- UpdatetODT_OFF_WRas well
- Added patches 2-4Chris Packham (4):
ddr: marvell: only assert M_ODT[0] on write for a single CS
ddr: marvell: use correct TREFI value
ddr: marvell: update additional ODT setting
ddr: marvell: update ddr controller init and freq
Quoted 8 lines
drivers/ddr/marvell/a38x/ddr3_init.h | 3 +- drivers/ddr/marvell/a38x/ddr3_topology_def.h | 3 +- drivers/ddr/marvell/a38x/ddr3_training.c | 50 +++++++++++++++--------- drivers/ddr/marvell/a38x/ddr3_training_db.c | 19 +++++---- drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c | 22 +++++++---- drivers/ddr/marvell/a38x/ddr3_training_static.c | 3 +- 6 files changed, 62 insertions(+), 38 deletions(-)
Big thanks for working on this. All patches:
Applied to u-boot-marvell/master.
Thanks,
Stefan