Skip to contentSkip to conversation

ddr: marvell: update DDR driver

PATCH New Chris Packham 2 participants 6 messages Jan 18, 2018 – Jan 20, 2018

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index 8cb08864c292..a4c75a9fa68f 100644
@@ -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
index e70ca4b42551..043143adf08a 100644
@@ -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
index 5101f3f38349..b73bbf4f1b0d 100644
@@ -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
#2 lore raw

PATCH v2 2/4 ddr: marvell: use correct TREFI value

In reply to Chris Packham

The 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
index 043143adf08a..2eb6e05783d4 100644
@@ -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
#3 lore raw

PATCH v2 3/4 ddr: marvell: update additional ODT setting

In reply to Chris Packham

The 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
index 56fce174d4c0..1fc173b60020 100644
@@ -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
#4 lore raw

PATCH v2 4/4 ddr: marvell: update ddr controller init and freq

In reply to Chris Packham

Update 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
index 64a0447dd15b..a17eca041878 100644
@@ -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
index 2eb6e05783d4..ef471e565efd 100644
@@ -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
index 861dfb19c366..0e11b434ab67 100644
@@ -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
#5 lore raw

v2 cover ddr: marvell: update DDR driver

In reply to Chris Packham

Hi 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:
- 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

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