On 29/8/2026 12:19 am, Kathpalia, Tanmay wrote:
On 8/21/2026 1:38 PM, [email protected] wrote:
From: Tze Yee Ng <[email protected]>
After reprogramming the PHY and releasing the DLL reset, the PHY/DLL
needs time to relock before the first data transfer is issued.
Add a 5ms settle delay at the end of sdhci_cdns6_phy_adj().
Fixes: fe11aa0b8ca3 ("mmc: sdhci-cadence: Add support for Cadence
sdmmc v6")
Signed-off-by: Tze Yee Ng <[email protected]>
---
drivers/mmc/sdhci-cadence6.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c
index 93cb19ecaaf..407eae96dd4 100644
--- a/drivers/mmc/sdhci-cadence6.c
+++ b/drivers/mmc/sdhci-cadence6.c
@@ -11,6 +11,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bug.h>
+#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/sizes.h>
@@ -302,6 +303,17 @@ int sdhci_cdns6_phy_adj(struct udevice *dev,
struct sdhci_cdns_plat *plat, u32 m
/* Set HRS07 register */
writel(sdhci_cdns6_ctrl_cfgs[3].val, plat->hrs_addr +
SDHCI_CDNS_HRS07);
+ /*
+ * Wait for the PHY/DLL to settle before the first data transfer.
+ * The HRS09.PHY_INIT_COMPLETE handshake polled above only covers
DLL
+ * relock, not the analog read/write path retiming that follows a
+ * timing change. Without this extra settle (together with the HRS05
+ * posted-write flush in sdhci_cdns6_write_phy_reg()) the first
Did you still see the DDR50 CRC with mdelay(5) and without patch 1?
Hi Tanmay,
Good question. I went back and isolated this properly. With the
corrected DDR50 read-strobe PHY timing in place
(https://patchwork.ozlabs.org/project/uboot/patch/2781fb330eb3af567882b68f709d6445865b028a.1787710268.git.tze.yee...@altera.com/),
DDR50 comes up cleanly without either change - no posted-write flush
(patch 1) and no 5ms settle (patch 2). I didn't isolate
mdelay(5)-without-flush specifically, since with the corrected timing
neither is needed.
Background on why I submitted patches 1 and 2: at the time I had not yet
found the correct read-strobe timing. With that (wrong) timing, adding
the posted-write flush and the 5ms settle made DDR50 pass on warm reset,
so they looked like a genuine fix. But DDR50 still failed on cold reset
with the same binaries. That warm-vs-cold inconsistency is what led me
to keep digging and eventually find the correct read-strobe PHY timing,
which fixes both warm and cold boots.
+ * transfer after a DDR50 mode switch intermittently returns a CRC
+ * error.
+ */
+ mdelay(5);
+
Where does 5 ms come from? A delay sweep on the board, or the Linux
usleep_range(5000, 5500)? Did you try other delays on U-Boot (1 ms /
2 ms / 10 ms, …) and see when it starts passing?
Honestly, it came from the Linux
SD6HC driver's usleep_range(5000, 5500), not from an independent U-Boot
delay sweep, so I don't have a measured "starts passing at X ms" curve
to justify the specific value. So I rather not keeping the 5ms settle as
there is no spec basis for that exact number, and my testing shows it
isn't needed.
Given the above, the real fix is the PHY timing, so this 2-patch series
can be dropped.
On the posted-write flush specifically, if you prefer to keep an
explicit completion guarantee, I propose to keep *only* the HRS05
read-back (patch1). On SD6HC, the PHY registers are not directly mapped;
they are accessed indirectly via HRS04 (address) + HRS05 (data), and
each HRS05 access generates a transaction to the Combo PHY over an APB
interface. Unlike SD4HC, HRS04/HRS05 expose no ACK/BUSY/DONE status bit,
so software has nothing to poll for completion. A read-back of HRS05, on
the other hand, forces an APB read round-trip to the PHY and so
guarantees the preceding write drained.
Let me know your preference.
Thanks,
Tze Yee