summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoyan Karatotev <boyan.karatotev@arm.com>2026-06-25 06:43:09 +0000
committerTrustedFirmware Code Review <review@review.trustedfirmware.org>2026-06-25 06:43:09 +0000
commit5da9bc26bb5bfbb466a092d2169f0c82ea49b938 (patch)
treeae22183291d2c655c9e46375d8c170c2b24b0845
parentd4809615cc25a7beb0fe292f8a66e3933783a149 (diff)
parent93307ca915830c9795440282a70923866f87bc3b (diff)
downloadarm-trusted-firmware-5da9bc26bb5bfbb466a092d2169f0c82ea49b938.tar.gz
arm-trusted-firmware-5da9bc26bb5bfbb466a092d2169f0c82ea49b938.zip
Merge changes from topic "nxp-ddr-lx2160-fixes" into integration
* changes: fix(nxp-ddr): adjust DDR_DSR2[2] per LX2160A RM fix(nxp-ddr): support SPD diffs between dual DIMMs
-rw-r--r--drivers/nxp/ddr/nxp-ddr/ddr.c78
-rw-r--r--drivers/nxp/ddr/nxp-ddr/ddr.mk8
-rw-r--r--drivers/nxp/ddr/nxp-ddr/ddrc.c15
3 files changed, 93 insertions, 8 deletions
diff --git a/drivers/nxp/ddr/nxp-ddr/ddr.c b/drivers/nxp/ddr/nxp-ddr/ddr.c
index 17c2bbb2a..4346ce646 100644
--- a/drivers/nxp/ddr/nxp-ddr/ddr.c
+++ b/drivers/nxp/ddr/nxp-ddr/ddr.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <inttypes.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -571,8 +572,85 @@ static int parse_spd(struct ddr_info *priv)
if (spd_idx != 0 && spd_checksum[0] !=
spd_checksum[spd_idx]) {
+#if defined(NXP_DDR_DUAL_DIMM_TOLERANT)
+ /* DDR4 SPD bytes that have impacts */
+ static const unsigned int timing_bytes[] = {
+
+ /* device key + organisation */
+ 2, /* mem_type DDR3 / DDR4 / LPDDR4 key */
+ 4, /* density_banks SDRAM density + bank groups */
+ 5, /* addressing row/col bit counts */
+ 6, /* package_type SDP / DDP / 3DS / QDP */
+ 7, /* opt_feature MAW, fault detect */
+ 8, /* thermal_ref refresh interval mode */
+ 9, /* oth_opt_features PPR / soft-PPR */
+ 11, /* module_vdd nominal voltage (DDR4 = 1.2 V) */
+ 12, /* organization ranks + DRAM width (x4/x8/x16) */
+ 13, /* bus_width primary bus width + ECC bit */
+ 14, /* therm_sensor on-DIMM temp-sensor presence */
+
+ /* DDR4 base timing block - MTB units (MTB = 125 ps) */
+ 17, /* timebases MTB/FTB select (0x00 on DDR4) */
+ 18, /* tck_min min SDRAM cycle time -> speed bin */
+ 19, /* tck_max max SDRAM cycle time */
+ 20, /* caslat_b1 supported CAS latencies, byte 1 */
+ 21, /* caslat_b2 supported CAS latencies, byte 2 */
+ 22, /* caslat_b3 supported CAS latencies, byte 3 */
+ 23, /* caslat_b4 supported CAS latencies, byte 4 */
+ 24, /* taa_min min CAS latency time -> CL pick */
+ 25, /* trcd_min RAS-to-CAS delay */
+ 26, /* trp_min row precharge time */
+ 27, /* tras_trc_ext upper nibbles for tRAS / tRC */
+ 28, /* tras_min_lsb active-to-precharge LSB */
+ 29, /* trc_min_lsb row-cycle time LSB */
+ 30, /* trfc1_min_lsb refresh recovery 1x mode LSB */
+ 31, /* trfc1_min_msb refresh recovery 1x mode MSB */
+ 32, /* trfc2_min_lsb refresh recovery 2x mode LSB */
+ 33, /* trfc2_min_msb refresh recovery 2x mode MSB */
+ 34, /* trfc4_min_lsb refresh recovery 4x mode LSB */
+ 35, /* trfc4_min_msb refresh recovery 4x mode MSB */
+ 36, /* tfaw_msb four-activate-window upper nibble */
+ 37, /* tfaw_min four-activate-window LSB */
+ 38, /* trrds_min act-to-act, different bank groups */
+ 39, /* trrdl_min act-to-act, same bank group */
+ 40, /* tccdl_min CAS-to-CAS, same bank group */
+
+ /* DDR4 fine timing - FTB units (1 ps), signed deltas */
+ 117, /* fine_tccdl_min adds to byte 40 (tccdl_min) */
+ 118, /* fine_trrdl_min adds to byte 39 (trrdl_min) */
+ 119, /* fine_trrds_min adds to byte 38 (trrds_min) */
+ 120, /* fine_trc_min adds to byte 27/29 (trc) */
+ 121, /* fine_trp_min adds to byte 26 (trp) */
+ 122, /* fine_trcd_min adds to byte 25 (trcd) */
+ 123, /* fine_taa_min adds to byte 24 (taa) */
+ 124, /* fine_tck_max adds to byte 19 (tck_max) */
+ 125, /* fine_tck_min adds to byte 18 (tck_min) */
+ };
+ const unsigned char *pa =
+ (const unsigned char *)&spd[0];
+ const unsigned char *pb =
+ (const unsigned char *)&spd[spd_idx];
+ bool timing_ok = true;
+ unsigned int t;
+
+ for (t = 0U; t < ARRAY_SIZE(timing_bytes); t++) {
+ unsigned int bx = timing_bytes[t];
+
+ if (pa[bx] != pb[bx]) {
+ ERROR("DDR SPD timing byte %u: 0x%02x vs 0x%02x\n",
+ bx, pa[bx], pb[bx]);
+ timing_ok = false;
+ }
+ }
+ if (!timing_ok) {
+ ERROR("Not identical DIMMs (timing-relevant mismatch).\n");
+ return -EINVAL;
+ }
+ NOTICE("DDR SPDs differ for non-timing bytes only -> accepting\n");
+#else
ERROR("Not identical DIMMs.\n");
return -EINVAL;
+#endif
}
conf->dimm_in_use[j] = 1;
valid_mask |= 1 << addr_idx;
diff --git a/drivers/nxp/ddr/nxp-ddr/ddr.mk b/drivers/nxp/ddr/nxp-ddr/ddr.mk
index be9163384..35d615dbd 100644
--- a/drivers/nxp/ddr/nxp-ddr/ddr.mk
+++ b/drivers/nxp/ddr/nxp-ddr/ddr.mk
@@ -74,6 +74,14 @@ ifeq ($(DEBUG_DDR_INPUT_CONFIG), yes)
$(eval $(call add_define, DEBUG_DDR_INPUT_CONFIG))
endif
+# Tolerant SPD compare workaround is for a dual-DIMM pair whose
+# base/module CRCs differ but whose timing-relevant bytes match
+# Default off: a CRC mismatch is a hard error unless a board
+# wants to be tolerant for such cases.
+ifeq ($(NXP_DDR_DUAL_DIMM_TOLERANT), yes)
+$(eval $(call add_define,NXP_DDR_DUAL_DIMM_TOLERANT))
+endif
+
DDR_CNTLR_SOURCES := $(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/ddr.c \
$(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/ddrc.c \
$(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/dimm.c \
diff --git a/drivers/nxp/ddr/nxp-ddr/ddrc.c b/drivers/nxp/ddr/nxp-ddr/ddrc.c
index 4133fac1a..24cc0775e 100644
--- a/drivers/nxp/ddr/nxp-ddr/ddrc.c
+++ b/drivers/nxp/ddr/nxp-ddr/ddrc.c
@@ -426,19 +426,18 @@ after_reset:
mb();
isb();
} else {
- /* wait for PHY complete */
- timeout = 40;
- while (((ddr_in32(&ddr->ddr_dsr2) & 0x4) != 0) &&
+ /* Wait up to 50 ms for DDR_DSR2[2] PHY_INIT_CMPLT to be set. */
+ timeout = 100;
+ while (((ddr_in32(&ddr->ddr_dsr2) & 0x4) == 0) &&
(timeout > 0)) {
udelay(500);
timeout--;
}
- if (timeout <= 0) {
- printf("PHY handshake timeout, ddr_dsr2 = %x\n",
- ddr_in32(&ddr->ddr_dsr2));
+ if (timeout > 0) {
+ debug("PHY init complete (DSR2[2]=1) in ~%d ms\n",
+ (100 - timeout) / 2);
} else {
- debug("PHY handshake completed, timer remains %d\n",
- timeout);
+ WARN("PHY init NOT complete after 50 ms (DSR2[2]=0) -> proceeding, training will report any real fault\n");
}
}