]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
sunxi: A523: add DDR3 DRAM support
authorMikhail Kalashnikov <iuncuim@gmail.com>
Fri, 3 Jan 2025 05:19:07 +0000 (08:19 +0300)
committerAndre Przywara <andre.przywara@arm.com>
Sun, 27 Jul 2025 22:11:08 +0000 (23:11 +0100)
Add reverse engineered code to add support for DDR3 DRAM chips on the
Allwinner A523 DRAM controller.
The timings are copying what boot0 set up on the X96QPro+ TV box, though
they seem quite suboptimal, with longer latencies that would be required
for DDR3-1600. The chips are also actually capable of DDR3-1833, so
there is room for future improvement.

Signed-off-by: Mikhail Kalashnikov <iuncuim@gmail.com>
[Andre: rework to copy from H616 DDR3 driver, calculate timings]
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
arch/arm/mach-sunxi/Kconfig
arch/arm/mach-sunxi/dram_sun55i_a523.c
arch/arm/mach-sunxi/dram_timings/Makefile
arch/arm/mach-sunxi/dram_timings/a523_ddr3.c [new file with mode: 0644]

index 8aa5f1b46bf0702511702b4c7ae898574082a444..8a19534c2ecf4430ebe55852c7122a70cf5acf59 100644 (file)
@@ -676,6 +676,14 @@ config SUNXI_DRAM_DDR2_V3S
        This option is only for the DDR2 memory chip which is co-packaged in
        Allwinner V3s SoC.
 
+config SUNXI_DRAM_A523_DDR3
+       bool "DDR3 DRAM chips on the A523/T527 DRAM controller"
+       select SUNXI_DRAM_DDR3
+       depends on DRAM_SUN55I_A523
+       help
+         This option is the DDR3 timing used by the stock boot0 by
+         Allwinner.
+
 config SUNXI_DRAM_A523_LPDDR4
        bool "LPDDR4 DRAM chips on the A523/T527 DRAM controller"
        select SUNXI_DRAM_LPDDR4
index a5c4fba77848c10bfa169fad4868ac8e7e9be882..30bbeb40d0b502937cb5b837020d983dc39752f2 100644 (file)
@@ -870,6 +870,24 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para,
        clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x48, 0xc0000000);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+       case SUNXI_DRAM_TYPE_DDR4:
+       case SUNXI_DRAM_TYPE_LPDDR3:
+               low = val & 0xff;
+               high = (val >> 8) & 0xff;
+
+               val = (high << 24) | (high << 16) | (high << 8) | high;
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x104);
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x108);
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x10c);
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x114);
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x118);
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x11c);
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x120);
+
+               val = (low << 24) | (low << 16) | (high << 8) | high;
+               writel(val, SUNXI_DRAM_PHY0_BASE + 0x11c);
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                low = val & 0xff;
                high = (val >> 8) & 0xff;
@@ -920,6 +938,26 @@ static bool mctl_phy_init(const struct dram_para *para,
        clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0xf00, val);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               val = 9;
+               val2 = 13;
+               break;
+       case SUNXI_DRAM_TYPE_DDR4:
+               if (config->clk <= 936) {
+                       val = 10;
+                       val2 = 14;
+               } else if (config->clk <= 1200) {
+                       val = 12;
+                       val2 = 16;
+               } else {
+                       val = 14;
+                       val2 = 18;
+               }
+               break;
+       case SUNXI_DRAM_TYPE_LPDDR3:
+               val = 8;
+               val2 = 14;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                if (config->clk <= 936) {
                        val = 10;
@@ -941,6 +979,36 @@ static bool mctl_phy_init(const struct dram_para *para,
        writel(0, SUNXI_DRAM_PHY0_BASE + 0x08);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               writel(0x150a0310, SUNXI_DRAM_PHY0_BASE + 0x54);
+               writel(0x13140816, SUNXI_DRAM_PHY0_BASE + 0x58);
+               writel(0x001c0d1b, SUNXI_DRAM_PHY0_BASE + 0x5c);
+               writel(0x050c1d1a, SUNXI_DRAM_PHY0_BASE + 0x60);
+               writel(0x0411060b, SUNXI_DRAM_PHY0_BASE + 0x64);
+               writel(0x09071217, SUNXI_DRAM_PHY0_BASE + 0x68);
+               writel(0x18190e01, SUNXI_DRAM_PHY0_BASE + 0x6c);
+               writel(0x020f1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+               break;
+       case SUNXI_DRAM_TYPE_DDR4:
+               writel(0x090c1c14, SUNXI_DRAM_PHY0_BASE + 0x54);
+               writel(0x1300060f, SUNXI_DRAM_PHY0_BASE + 0x58);
+               writel(0x12030807, SUNXI_DRAM_PHY0_BASE + 0x5c);
+               writel(0x0b100a02, SUNXI_DRAM_PHY0_BASE + 0x60);
+               writel(0x1a110e05, SUNXI_DRAM_PHY0_BASE + 0x64);
+               writel(0x0d041617, SUNXI_DRAM_PHY0_BASE + 0x68);
+               writel(0x1819011b, SUNXI_DRAM_PHY0_BASE + 0x6c);
+               writel(0x151d1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+               break;
+       case SUNXI_DRAM_TYPE_LPDDR3:
+               writel(0x010a1a0f, SUNXI_DRAM_PHY0_BASE + 0x54);
+               writel(0x10081b07, SUNXI_DRAM_PHY0_BASE + 0x58);
+               writel(0x11061c12, SUNXI_DRAM_PHY0_BASE + 0x5c);
+               writel(0x00131409, SUNXI_DRAM_PHY0_BASE + 0x60);
+               writel(0x15030e16, SUNXI_DRAM_PHY0_BASE + 0x64);
+               writel(0x0b0c0d17, SUNXI_DRAM_PHY0_BASE + 0x68);
+               writel(0x18190204, SUNXI_DRAM_PHY0_BASE + 0x6c);
+               writel(0x051d1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                writel(0x00010203, SUNXI_DRAM_PHY0_BASE + 0x54);
                writel(0x04050607, SUNXI_DRAM_PHY0_BASE + 0x58);
@@ -961,6 +1029,15 @@ static bool mctl_phy_init(const struct dram_para *para,
                mctl_phy_ca_bit_delay_compensation(para, config);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               val = 0x2bbd4900;
+               break;
+       case SUNXI_DRAM_TYPE_DDR4:
+               val = 0x3841b800;
+               break;
+       case SUNXI_DRAM_TYPE_LPDDR3:
+               val = 0x19016300;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                val = 0x18fd6300;
                break;
@@ -972,6 +1049,15 @@ static bool mctl_phy_init(const struct dram_para *para,
        clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0x70);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               val = 0x20;
+               break;
+       case SUNXI_DRAM_TYPE_DDR4:
+               val = 0x40;
+               break;
+       case SUNXI_DRAM_TYPE_LPDDR3:
+               val = 0x30;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                val = 0x50;
                break;
@@ -1002,17 +1088,22 @@ static bool mctl_phy_init(const struct dram_para *para,
        udelay(10);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               val = para->tpr6 & 0xff;
+               break;
+       case SUNXI_DRAM_TYPE_DDR4:
+               val = para->tpr6 >> 8 & 0xff;
+               break;
+       case SUNXI_DRAM_TYPE_LPDDR3:
+               val = para->tpr6 >> 16 & 0xff;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
-               val = para->tpr6 >> 24 & 0xff;
-               if (val)
-                       val <<= 1;
-               else
-                       val = 0x33;
+               val = para->tpr6 >> 24;
                break;
        default:
                panic("This DRAM setup is currently not supported.\n");
        };
-       val <<= 23;
+       val <<= 24;
 
        clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x300, 0xff800060, val | 0x40);
        clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x600, 0xff800060, val | 0x40);
@@ -1077,6 +1168,23 @@ static bool mctl_phy_init(const struct dram_para *para,
        mctl_await_completion(&mctl_ctl->swstat, 1, 1);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               writel(0x1f14, &mctl_ctl->mrctrl1);
+               writel(0x800000f0, &mctl_ctl->mrctrl0);
+               mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+               writel(4, &mctl_ctl->mrctrl1);
+               writel(0x800010f0, &mctl_ctl->mrctrl0);
+               mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+               writel(0x20, &mctl_ctl->mrctrl1);
+               writel(0x800020f0, &mctl_ctl->mrctrl0);
+               mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+               writel(0, &mctl_ctl->mrctrl1);
+               writel(0x800030f0, &mctl_ctl->mrctrl0);
+               mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                if (config->clk <= 936) {
                        mr1 = 0x34;
@@ -1207,6 +1315,9 @@ static bool mctl_ctrl_init(const struct dram_para *para,
 
        reg_val = MSTR_ACTIVE_RANKS(config->ranks);
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               reg_val |= MSTR_BURST_LENGTH(8) | MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                reg_val |= MSTR_BURST_LENGTH(16) | MSTR_DEVICETYPE_LPDDR4;
                break;
@@ -1225,6 +1336,9 @@ static bool mctl_ctrl_init(const struct dram_para *para,
                writel(0x0201, &mctl_ctl->odtmap);
 
        switch (para->type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               reg_val = 0x06000400;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                reg_val = 0x04000400;
                break;
@@ -1359,7 +1473,11 @@ static unsigned long long mctl_calc_size(const struct dram_config *config)
 }
 
 static const struct dram_para para = {
+#ifdef CONFIG_SUNXI_DRAM_A523_DDR3
+       .type = SUNXI_DRAM_TYPE_DDR3,
+#elif defined(CONFIG_SUNXI_DRAM_A523_LPDDR4)
        .type = SUNXI_DRAM_TYPE_LPDDR4,
+#endif
        .dx_odt = CONFIG_DRAM_SUNXI_DX_ODT,
        .dx_dri = CONFIG_DRAM_SUNXI_DX_DRI,
        .ca_dri = CONFIG_DRAM_SUNXI_CA_DRI,
@@ -1433,6 +1551,12 @@ unsigned long sunxi_dram_init(void)
 
        config.clk = 360;
        switch (para.type) {
+       case SUNXI_DRAM_TYPE_DDR3:
+               config.odt_en = 0x90909090;
+               config.tpr11 = 0x8f919190;
+               config.tpr12 = 0x22222723;
+               config.tpr14 = 0x48484848;
+               break;
        case SUNXI_DRAM_TYPE_LPDDR4:
                config.odt_en = 0x84848484;
                config.tpr11 = 0x9a9a9a9a;
index 41fee509d5d855a6d2eea891504f5988896dc3c9..5de9fd5aab4bc6f05c0edc5767b49abf57def4dd 100644 (file)
@@ -8,4 +8,5 @@ obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3)    += h616_lpddr3.o
 obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4)   += h616_lpddr4_2133.o
 obj-$(CONFIG_SUNXI_DRAM_A133_DDR4)     += a133_ddr4.o
 obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4)   += a133_lpddr4.o
+obj-$(CONFIG_SUNXI_DRAM_A523_DDR3)     += a523_ddr3.o
 obj-$(CONFIG_SUNXI_DRAM_A523_LPDDR4)   += a523_lpddr4.o
diff --git a/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
new file mode 100644 (file)
index 0000000..6e140bb
--- /dev/null
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun55i A523 DDR3 timings, as programmed by Allwinner's boot0 on
+ * the X96QPro+ TV box. As usual very conservative timings, but probably
+ * the most compatible and reliable.
+ *
+ * (C) Copyright 2024 Mikhail Kalashnikov <iuncuim@gmail.com>
+ *   Based on H616 DDR3 timings:
+ *   (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+
+void mctl_set_timing_params(u32 clk)
+{
+       struct sunxi_mctl_ctl_reg * const mctl_ctl =
+                       (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+       /*
+        *                                      formulas and constraints as of
+        *                                      JEDEC DDR3 specification, for
+        *                                      DDR3-1600, per JESD79-3F
+        */
+       u8 tccd         = 2;                            /* 4nCK */
+       u8 tfaw         = ns_to_t(50, clk);
+       u8 trrd         = max(ns_to_t(6, clk), 4);      /* max(6 ns, 4nCK) */
+       u8 twtr         = max(ns_to_t(8, clk), 4);      /* max(7.5 ns, 4nCK) */
+       u8 trcd         = ns_to_t(15, clk);             /* 13.5 ns */
+       u8 trc          = ns_to_t(53, clk);
+       u8 txp          = max(ns_to_t(8, clk), 2);      /* max(6 ns, 3nCK) */
+       u8 trtp         = max(ns_to_t(8, clk), 4);      /* max(7.5 ns, 4nCK) */
+       u8 trp          = ns_to_t(15, clk);             /* >= 13.75 ns */
+       u8 tras         = ns_to_t(38, clk);
+       u16 trefi       = ns_to_t(11350, clk) / 32;
+       u16 trfc        = ns_to_t(360, clk);            /* 160 ns for 2Gb */
+       u16 txsr        = 4;
+
+       u8 tmrw         = 0;
+       u8 tmrd         = 4;                            /* 4nCK */
+
+       u8 tmod         = max(ns_to_t(15, clk), 12);    /* max(15 ns, 12nCK) */
+       u8 tcke         = max(ns_to_t(6, clk), 4);      /* max(5.625 ns, 3nCK)*/
+       u8 tcksrx       = max(ns_to_t(10, clk), 4);     /* max(10 ns, 5nCK) */
+       u8 tcksre       = max(ns_to_t(10, clk), 4);     /* max(10 ns, 5nCK) */
+       u8 trasmax      = (clk / 2) / 15;               /* tREFI * 9 */
+
+       /*
+        * TODO: support multiple DDR3 speed grades, these values below match
+        * the worst case for DDR3-2133, so should be good for all frequencies,
+        * but use the most conversative timings.
+        * DDR3-1866 (DRAM_CLK=912) should also work, or tcl=6 and tcwl=4 with
+        * DRAM_CLK=792. Maybe even the combination of both, depending on the
+        * particular device.
+        */
+       u8 tcl          = 7;                    /* CAS latency: 14 */
+       u8 tcwl         = 5;                    /* CAS write latency: 10 */
+       u8 t_rdata_en   = 9;
+       u8 tphy_wrlat   = 5;
+       u8 twr          = 7;
+
+       u8 tckesr       = tcke + 1;                     /* tCKE(min) + 1nCK */
+
+       u8 twtp         = twr + 2 + tcwl;
+       u8 twr2rd       = twtr + 2 + tcwl;      /* (WL + BL / 2 + tWTR) / 2 */
+       u8 trd2wr       = tcl + 3 - tcwl;
+       u8 txs          = ns_to_t(360, clk) / 32;       /* max(5nCK,tRFC+10ns)*/
+       u8 txsdll       = 512 / 32;                     /* 512 nCK */
+       u8 txsabort     = 4;
+       u8 txsfast      = 4;
+
+       /* set DRAM timing */
+       writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
+              &mctl_ctl->dramtmg[0]);
+       writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
+       writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
+              &mctl_ctl->dramtmg[2]);
+       writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
+       writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
+              &mctl_ctl->dramtmg[4]);
+       writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
+              &mctl_ctl->dramtmg[5]);
+       /* Value suggested by ZynqMP manual and used by libdram */
+       writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
+       writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
+              &mctl_ctl->dramtmg[8]);
+       writel(0x00020208, &mctl_ctl->dramtmg[9]);
+       writel(0xe0c05, &mctl_ctl->dramtmg[10]);
+       writel(0x440c021c, &mctl_ctl->dramtmg[11]);
+       writel(8, &mctl_ctl->dramtmg[12]);
+       writel(0xa100002, &mctl_ctl->dramtmg[13]);
+       writel(txsr, &mctl_ctl->dramtmg[14]);
+
+       clrsetbits_le32(&mctl_ctl->init[0], 0xc0000fff, 0x156);
+       writel(0x01f20000, &mctl_ctl->init[1]);
+       writel(0x00001700, &mctl_ctl->init[2]);
+       writel(0, &mctl_ctl->dfimisc);
+       writel(0x1f140004, &mctl_ctl->init[3]);
+       writel(0x00200000, &mctl_ctl->init[4]);
+       writel(0, &mctl_ctl->init[6]);  /* ? */
+       writel(0, &mctl_ctl->init[7]);  /* ? */
+
+       clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
+
+       /* Configure DFI timing */
+       writel(tphy_wrlat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
+              &mctl_ctl->dfitmg0);
+       writel(0x100202, &mctl_ctl->dfitmg1);
+
+       /* set refresh timing */
+       trfc = 0;       /* as written so by boot0 */
+       writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}