udelay(200);
                setbits_le32(&dram->mcr, DRAM_MCR_RESET);
        }
+       /* After the RESET pin is de-asserted, the DDR3 spec requires to wait
+        * for additional 500 us before driving the CKE pin (Clock Enable)
+        * high. The duration of this delay can be configured in the SDR_IDCR
+        * (Initialization Delay Configuration Register) and applied
+        * automatically by the DRAM controller during the DDR3 initialization
+        * step. But SDR_IDCR has limited range on sun4i/sun5i hardware and
+        * can't provide sufficient delay at DRAM clock frequencies higher than
+        * 524 MHz (while Allwinner A13 supports DRAM clock frequency up to
+        * 533 MHz according to the datasheet). Additionally, there is no
+        * official documentation for the SDR_IDCR register anywhere, and
+        * there is always a chance that we are interpreting it wrong.
+        * Better be safe than sorry, so add an explicit delay here. */
+       udelay(500);
 }
 
 static void mctl_set_drive(void)
        writel(0x16510000, &dram->ppwrsctl);
 }
 
+/*
+ * After the DRAM is powered up or reset, the DDR3 spec requires to wait at
+ * least 500 us before driving the CKE pin (Clock Enable) high. The dram->idct
+ * (SDR_IDCR) register appears to configure this delay, which gets applied
+ * right at the time when the DRAM initialization is activated in the
+ * 'mctl_ddr3_initialize' function.
+ */
+static void mctl_set_cke_delay(void)
+{
+       struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
+
+       /* The CKE delay is represented in DRAM clock cycles, multiplied by N
+        * (where N=2 for sun4i/sun5i and N=3 for sun7i). Here it is set to
+        * the maximum possible value 0x1ffff, just like in the Allwinner's
+        * boot0 bootloader. The resulting delay value is somewhere between
+        * ~0.4 ms (sun5i with 648 MHz DRAM clock speed) and ~1.1 ms (sun7i
+        * with 360 MHz DRAM clock speed). */
+       setbits_le32(&dram->idcr, 0x1ffff);
+}
+
+/*
+ * This triggers the DRAM initialization. It performs sending the mode registers
+ * to the DRAM among other things. Very likely the ZQCL command is also getting
+ * executed (to do the initial impedance calibration on the DRAM side of the
+ * wire). The memory controller and the PHY must be already configured before
+ * calling this function.
+ */
+static void mctl_ddr3_initialize(void)
+{
+       struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
+       setbits_le32(&dram->ccr, DRAM_CCR_INIT);
+       await_completion(&dram->ccr, DRAM_CCR_INIT);
+}
+
 unsigned long dramc_init(struct dram_para *para)
 {
        struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
        writel(reg_val, &dram->zqcr0);
 #endif
 
-#ifdef CONFIG_SUN7I
-       /* Set CKE Delay to about 1ms */
-       setbits_le32(&dram->idcr, 0x1ffff);
-#endif
+       mctl_set_cke_delay();
 
 #ifdef CONFIG_SUN7I
        mctl_ddr3_reset();
        if (para->tpr4 & 0x1)
                setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T);
 #endif
-       /* reset external DRAM */
-       setbits_le32(&dram->ccr, DRAM_CCR_INIT);
-       await_completion(&dram->ccr, DRAM_CCR_INIT);
+       /* initialize external DRAM */
+       mctl_ddr3_initialize();
 
        /* scan read pipe value */
        mctl_itm_enable();