[PATCH u-boot-marvell 11/16] arm: mvebu: turris_omnia: Read DDR speed from EEPROM

Marek Behún kabel at kernel.org
Tue Jun 18 17:34:34 CEST 2024


Some Turris Omnia boards experience memory issues, and by
experimentation we found that some of these issues can be solved by
slowing DDR speed.

Add a new field in the extended EEPROM information structure, ddr_speed.
Support several values in this field (for now 1066F, 1333H, and the
default, 1600K) and use it to overwrite the DDR topology parameters
used by the DDR training algorithm.

Signed-off-by: Marek Behún <kabel at kernel.org>
---
 board/CZ.NIC/turris_omnia/eeprom.c       | 41 +++++++++-
 board/CZ.NIC/turris_omnia/turris_omnia.c | 99 +++++++++++++++++++++++-
 2 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/board/CZ.NIC/turris_omnia/eeprom.c b/board/CZ.NIC/turris_omnia/eeprom.c
index ea13e95b37..32572481ce 100644
--- a/board/CZ.NIC/turris_omnia/eeprom.c
+++ b/board/CZ.NIC/turris_omnia/eeprom.c
@@ -55,12 +55,49 @@ static int eeprom_field_update_region(struct eeprom_field *field, char *value)
 	return 0;
 }
 
+static void eeprom_field_print_ddr_speed(const struct eeprom_field *field)
+{
+	printf(PRINT_FIELD_SEGMENT, field->name);
+
+	if (field->buf[0] == '\0' || field->buf[0] == 0xff)
+		puts("(empty, defaults to 1600K)\n");
+	else
+		printf("%.5s\n", field->buf);
+}
+
+bool omnia_valid_ddr_speed(const char *name);
+void omnia_print_ddr_speeds(void);
+
+static int eeprom_field_update_ddr_speed(struct eeprom_field *field,
+					 char *value)
+{
+	if (value[0] == '\0') {
+		/* setting default value */
+		memset(field->buf, 0xff, field->size);
+
+		return 0;
+	}
+
+	if (!omnia_valid_ddr_speed(value)) {
+		printf("%s: invalid setting, supported values are:\n  ",
+		       field->name);
+		omnia_print_ddr_speeds();
+
+		return -1;
+	}
+
+	strncpy(field->buf, value, field->size);
+
+	return 0;
+}
+
 static struct eeprom_field omnia_layout[] = {
 	_DEF_FIELD("Magic constant", 4, bin),
 	_DEF_FIELD("RAM size in GB", 4, ramsz),
 	_DEF_FIELD("Wi-Fi Region", 4, region),
 	_DEF_FIELD("CRC32 checksum", 4, bin),
-	_DEF_FIELD("Extended reserved fields", 44, reserved),
+	_DEF_FIELD("DDR speed", 5, ddr_speed),
+	_DEF_FIELD("Extended reserved fields", 39, reserved),
 	_DEF_FIELD("Extended CRC32 checksum", 4, bin),
 };
 
@@ -96,7 +133,7 @@ static int omnia_update_field(struct eeprom_layout *layout, char *field_name,
 	}
 
 	if (field < ext_crc_field) {
-		u32 crc = crc32(0, layout->data, 44);
+		u32 crc = crc32(0, layout->data, 60);
 		put_unaligned_le32(crc, ext_crc_field->buf);
 	}
 
diff --git a/board/CZ.NIC/turris_omnia/turris_omnia.c b/board/CZ.NIC/turris_omnia/turris_omnia.c
index c2f91b762f..544784e860 100644
--- a/board/CZ.NIC/turris_omnia/turris_omnia.c
+++ b/board/CZ.NIC/turris_omnia/turris_omnia.c
@@ -431,7 +431,8 @@ struct omnia_eeprom {
 	u32 crc;
 
 	/* second part (only considered if crc2 is not all-ones) */
-	u8 reserved[44];
+	char ddr_speed[5];
+	u8 reserved[39];
 	u32 crc2;
 };
 
@@ -520,6 +521,26 @@ static int omnia_get_ram_size_gb(void)
 	return ram_size;
 }
 
+static const char *omnia_get_ddr_speed(void)
+{
+	struct omnia_eeprom oep;
+	static char speed[sizeof(oep.ddr_speed) + 1];
+
+	if (!omnia_read_eeprom(&oep))
+		return NULL;
+
+	if (!is_omnia_eeprom_second_part_valid(&oep))
+		return NULL;
+
+	if (!oep.ddr_speed[0] || oep.ddr_speed[0] == 0xff)
+		return NULL;
+
+	memcpy(&speed, &oep.ddr_speed, sizeof(oep.ddr_speed));
+	speed[sizeof(speed) - 1] = '\0';
+
+	return speed;
+}
+
 static const char * const omnia_get_mcu_type(void)
 {
 	static char result[] = "xxxxxxx (with peripheral resets)";
@@ -634,12 +655,84 @@ static struct mv_ddr_topology_map board_topology_map_2g = {
 	{0}				/* timing parameters */
 };
 
+static const struct omnia_ddr_speed {
+	char name[5];
+	u8 speed_bin;
+	u8 freq;
+} omnia_ddr_speeds[] = {
+	{ "1066F", SPEED_BIN_DDR_1066F, MV_DDR_FREQ_533 },
+	{ "1333H", SPEED_BIN_DDR_1333H, MV_DDR_FREQ_667 },
+	{ "1600K", SPEED_BIN_DDR_1600K, MV_DDR_FREQ_800 },
+};
+
+static const struct omnia_ddr_speed *find_ddr_speed_setting(const char *name)
+{
+	for (int i = 0; i < ARRAY_SIZE(omnia_ddr_speeds); ++i)
+		if (!strncmp(name, omnia_ddr_speeds[i].name, 5))
+			return &omnia_ddr_speeds[i];
+
+	return NULL;
+}
+
+bool omnia_valid_ddr_speed(const char *name)
+{
+	return find_ddr_speed_setting(name) != NULL;
+}
+
+void omnia_print_ddr_speeds(void)
+{
+	for (int i = 0; i < ARRAY_SIZE(omnia_ddr_speeds); ++i)
+		printf("%.5s%s", omnia_ddr_speeds[i].name,
+		       i == ARRAY_SIZE(omnia_ddr_speeds) - 1 ? "\n" : ", ");
+}
+
+static void fixup_speed_in_ddr_topology(struct mv_ddr_topology_map *topology)
+{
+	typeof(topology->interface_params[0]) *params;
+	const struct omnia_ddr_speed *setting;
+	const char *speed;
+	static bool done;
+
+	if (done)
+		return;
+
+	done = true;
+
+	speed = omnia_get_ddr_speed();
+	if (!speed)
+		return;
+
+	setting = find_ddr_speed_setting(speed);
+	if (!setting) {
+		printf("Unsupported value %s for DDR3 speed in EEPROM!\n",
+		       speed);
+		return;
+	}
+
+	params = &topology->interface_params[0];
+
+	/* don't inform if we are not changing the speed from the default one */
+	if (params->speed_bin_index == setting->speed_bin)
+		return;
+
+	printf("Fixing up DDR3 speed (EEPROM defines %s)\n", speed);
+
+	params->speed_bin_index = setting->speed_bin;
+	params->memory_freq = setting->freq;
+}
+
 struct mv_ddr_topology_map *mv_ddr_topology_map_get(void)
 {
+	struct mv_ddr_topology_map *topology;
+
 	if (omnia_get_ram_size_gb() == 2)
-		return &board_topology_map_2g;
+		topology = &board_topology_map_2g;
 	else
-		return &board_topology_map_1g;
+		topology = &board_topology_map_1g;
+
+	fixup_speed_in_ddr_topology(topology);
+
+	return topology;
 }
 
 static int set_regdomain(void)
-- 
2.44.2



More information about the U-Boot mailing list