[PATCH 0/5] sunxi: H616: improve DRAM setup and add LPDDR3 support

Hi,
this series picks up on Mikhail's H616 LPDDR3 DRAM support (many thanks again for sending that!), and prepends some patches that help to keep the code size down. This was triggered by Mikhail's patches creating bigger binaries, even though the DRAM type is known at compile time, and the compiler should optimise out the unused code parts. Looking at the disassembly I found some issues that this series fixes: - One function was not declared static, so was issued separately in the object file (patch 1/5). - Many functions do not change the values in the struct that gets passed in, so we should mark this pointer as "const", to help the compiler with the "constant propagation" optimisation (patch 2/5). - This alone does not really help, as this structure contains both variable and fixed fields. Patch 3/5 splits that up, so we can define one struct as "static const", which finally convinces the compiler to fold the compile-time constants into the code, and removes the code for the unused DRAM variant, along with other code parts. Even without the LPDDR3 support this reduces the code size by about 2KB(!), and allows the LPDDR3 patches to come for free, code-size wise. - The final two patches are Mikhail's patches, slightly massaged: white space issue, keeping the static const phy_init[] array, rebased on top of the "const" work above. I haven't reviewed the patch in anger, though.
Please have a look and test.
Cheers, Andre
Andre Przywara (3): sunxi: dram: make MBUS configuration functions static sunxi: H616: dram: const-ify DRAM function parameters sunxi: H616: dram: split struct dram_para
iuncuim (2): sunxi: H616: add DRAM type selection sunxi: H616: add LPDDR3 DRAM support
.../include/asm/arch-sunxi/dram_sun50i_h616.h | 12 +- arch/arm/mach-sunxi/Kconfig | 18 +- arch/arm/mach-sunxi/dram_sun50i_h6.c | 3 +- arch/arm/mach-sunxi/dram_sun50i_h616.c | 383 +++++++++++------- arch/arm/mach-sunxi/dram_sunxi_dw.c | 18 +- arch/arm/mach-sunxi/dram_timings/Makefile | 4 +- .../mach-sunxi/dram_timings/h616_ddr3_1333.c | 2 +- .../arm/mach-sunxi/dram_timings/h616_lpddr3.c | 95 +++++ configs/orangepi_zero2_defconfig | 1 + configs/x96_mate_defconfig | 1 + 10 files changed, 372 insertions(+), 165 deletions(-) create mode 100644 arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c

The usage of the C keyword "inline" seems to be a common misunderstanding: it's a *hint* only, and modern compilers will inline (or not) functions based on their own judgement and provided compiler options. So while marking functions as "inline" does not do much, missing the "static" keyword will force to compiler to spell out a version of the function for potential external callers, which actually increases the code size (though hopefully the linker will drop the function).
Change the "inline" attribute for the mbus_configure_port() functions in some Allwinner DRAM drivers to "static", so that the explicit version can actually be dropped from the object file, reducing the code size.
"static inline" has a use case in header files, where it avoids a warning if a .c file including this header does not use the particular function. In a .c file itself "static inline" is not useful otherwise, so just use static here as well.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/dram_sun50i_h6.c | 3 ++- arch/arm/mach-sunxi/dram_sun50i_h616.c | 2 +- arch/arm/mach-sunxi/dram_sunxi_dw.c | 18 +++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index b332f3a3e4a..bff2e42513c 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -93,7 +93,8 @@ enum { MBUS_QOS_HIGH, MBUS_QOS_HIGHEST }; -inline void mbus_configure_port(u8 port, + +static void mbus_configure_port(u8 port, bool bwlimit, bool priority, u8 qos, diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 1f9416d6eaf..02f6b2a06d4 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -31,7 +31,7 @@ enum { MBUS_QOS_HIGHEST };
-inline void mbus_configure_port(u8 port, +static void mbus_configure_port(u8 port, bool bwlimit, bool priority, u8 qos, diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c index 4af5922f334..9382d3d0be8 100644 --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c @@ -81,15 +81,15 @@ enum { MBUS_QOS_HIGHEST };
-static inline void mbus_configure_port(u8 port, - bool bwlimit, - bool priority, - u8 qos, /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */ - u8 waittime, /* 0 .. 0xf */ - u8 acs, /* 0 .. 0xff */ - u16 bwl0, /* 0 .. 0xffff, bandwidth limit in MB/s */ - u16 bwl1, - u16 bwl2) +static void mbus_configure_port(u8 port, + bool bwlimit, + bool priority, + u8 qos, /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */ + u8 waittime, /* 0 .. 0xf */ + u8 acs, /* 0 .. 0xff */ + u16 bwl0, /* 0 .. 0xffff, bandwidth limit in MB/s */ + u16 bwl1, + u16 bwl2) { struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;

Dne sreda, 07. junij 2023 ob 02:07:41 CEST je Andre Przywara napisal(a):
The usage of the C keyword "inline" seems to be a common misunderstanding: it's a *hint* only, and modern compilers will inline (or not) functions based on their own judgement and provided compiler options. So while marking functions as "inline" does not do much, missing the "static" keyword will force to compiler to spell out a version of the function for potential external callers, which actually increases the code size (though hopefully the linker will drop the function).
Change the "inline" attribute for the mbus_configure_port() functions in some Allwinner DRAM drivers to "static", so that the explicit version can actually be dropped from the object file, reducing the code size.
"static inline" has a use case in header files, where it avoids a warning if a .c file including this header does not use the particular function. In a .c file itself "static inline" is not useful otherwise, so just use static here as well.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Nice catch! Could it be put into common functions? These functions look the same. Anyway:
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej
arch/arm/mach-sunxi/dram_sun50i_h6.c | 3 ++- arch/arm/mach-sunxi/dram_sun50i_h616.c | 2 +- arch/arm/mach-sunxi/dram_sunxi_dw.c | 18 +++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index b332f3a3e4a..bff2e42513c 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -93,7 +93,8 @@ enum { MBUS_QOS_HIGH, MBUS_QOS_HIGHEST }; -inline void mbus_configure_port(u8 port,
+static void mbus_configure_port(u8 port, bool bwlimit, bool priority, u8 qos, diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 1f9416d6eaf..02f6b2a06d4 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -31,7 +31,7 @@ enum { MBUS_QOS_HIGHEST };
-inline void mbus_configure_port(u8 port, +static void mbus_configure_port(u8 port, bool bwlimit, bool priority, u8 qos, diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c index 4af5922f334..9382d3d0be8 100644 --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c @@ -81,15 +81,15 @@ enum { MBUS_QOS_HIGHEST };
-static inline void mbus_configure_port(u8 port,
bool bwlimit,
bool priority,
u8 qos, /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */
u8 waittime, /* 0 .. 0xf */
u8 acs, /* 0 .. 0xff */
u16 bwl0, /* 0 .. 0xffff, bandwidth limit in MB/s */
u16 bwl1,
u16 bwl2)
+static void mbus_configure_port(u8 port,
bool bwlimit,
bool priority,
u8 qos, /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */
u8 waittime, /* 0 .. 0xf */
u8 acs, /* 0 .. 0xff */
u16 bwl0, /* 0 .. 0xffff, bandwidth limit in MB/s */
u16 bwl1,
u16 bwl2)
{ struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;

There are quite some functions in the Allwinner H616 DRAM "driver", some of them actually change the parameters in the structure passed to them, but many are actually not. To increase the optimisation potential for the code, mark those functions that just read members of the passed dram_para struct as "const". This in itself does not decrease the code size, but lays the groundwork for future changes doing so.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- .../include/asm/arch-sunxi/dram_sun50i_h616.h | 2 +- arch/arm/mach-sunxi/dram_sun50i_h616.c | 30 +++++++++---------- .../mach-sunxi/dram_timings/h616_ddr3_1333.c | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h index 6db869c0985..34d19f71438 100644 --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h @@ -171,6 +171,6 @@ static inline int ns_to_t(int nanoseconds) return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000); }
-void mctl_set_timing_params(struct dram_para *para); +void mctl_set_timing_params(const struct dram_para *para);
#endif /* _SUNXI_DRAM_SUN50I_H616_H */ diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 02f6b2a06d4..e0ee764cec7 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -92,7 +92,7 @@ static void mctl_set_master_priority(void) dmb(); }
-static void mctl_sys_init(struct dram_para *para) +static void mctl_sys_init(u32 clk_rate) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; @@ -114,7 +114,7 @@ static void mctl_sys_init(struct dram_para *para)
/* Set PLL5 rate to doubled DRAM clock rate */ writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN | CCM_PLL5_OUT_EN | - CCM_PLL5_CTRL_N(para->clk * 2 / 24), &ccm->pll5_cfg); + CCM_PLL5_CTRL_N(clk_rate * 2 / 24), &ccm->pll5_cfg); mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK);
/* Configure DRAM mod clock */ @@ -141,7 +141,7 @@ static void mctl_sys_init(struct dram_para *para) writel(0x8000, &mctl_ctl->clken); }
-static void mctl_set_addrmap(struct dram_para *para) +static void mctl_set_addrmap(const struct dram_para *para) { struct sunxi_mctl_ctl_reg * const mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; @@ -234,7 +234,7 @@ static const u8 phy_init[] = { 0x09, 0x05, 0x18 };
-static void mctl_phy_configure_odt(struct dram_para *para) +static void mctl_phy_configure_odt(const struct dram_para *para) { unsigned int val;
@@ -281,7 +281,7 @@ static void mctl_phy_configure_odt(struct dram_para *para) dmb(); }
-static bool mctl_phy_write_leveling(struct dram_para *para) +static bool mctl_phy_write_leveling(const struct dram_para *para) { bool result = true; u32 val; @@ -336,7 +336,7 @@ static bool mctl_phy_write_leveling(struct dram_para *para) return result; }
-static bool mctl_phy_read_calibration(struct dram_para *para) +static bool mctl_phy_read_calibration(const struct dram_para *para) { bool result = true; u32 val, tmp; @@ -395,7 +395,7 @@ static bool mctl_phy_read_calibration(struct dram_para *para) return result; }
-static bool mctl_phy_read_training(struct dram_para *para) +static bool mctl_phy_read_training(const struct dram_para *para) { u32 val1, val2, *ptr1, *ptr2; bool result = true; @@ -484,7 +484,7 @@ static bool mctl_phy_read_training(struct dram_para *para) return result; }
-static bool mctl_phy_write_training(struct dram_para *para) +static bool mctl_phy_write_training(const struct dram_para *para) { u32 val1, val2, *ptr1, *ptr2; bool result = true; @@ -572,7 +572,7 @@ static bool mctl_phy_write_training(struct dram_para *para) return result; }
-static void mctl_phy_bit_delay_compensation(struct dram_para *para) +static void mctl_phy_bit_delay_compensation(const struct dram_para *para) { u32 *ptr, val; int i; @@ -773,7 +773,7 @@ static void mctl_phy_bit_delay_compensation(struct dram_para *para) } }
-static void mctl_phy_ca_bit_delay_compensation(struct dram_para *para) +static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para) { u32 val, *ptr; int i; @@ -822,7 +822,7 @@ static void mctl_phy_ca_bit_delay_compensation(struct dram_para *para) } }
-static bool mctl_phy_init(struct dram_para *para) +static bool mctl_phy_init(const struct dram_para *para) { struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; @@ -991,7 +991,7 @@ static bool mctl_phy_init(struct dram_para *para) return true; }
-static bool mctl_ctrl_init(struct dram_para *para) +static bool mctl_ctrl_init(const struct dram_para *para) { struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; @@ -1073,9 +1073,9 @@ static bool mctl_ctrl_init(struct dram_para *para) return true; }
-static bool mctl_core_init(struct dram_para *para) +static bool mctl_core_init(const struct dram_para *para) { - mctl_sys_init(para); + mctl_sys_init(para->clk);
return mctl_ctrl_init(para); } @@ -1147,7 +1147,7 @@ static void mctl_auto_detect_dram_size(struct dram_para *para) } }
-static unsigned long mctl_calc_size(struct dram_para *para) +static unsigned long mctl_calc_size(const struct dram_para *para) { u8 width = para->bus_full_width ? 4 : 2;
diff --git a/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c b/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c index eea4d6abec8..232b4fe2df7 100644 --- a/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c +++ b/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c @@ -15,7 +15,7 @@ #include <asm/arch/dram.h> #include <asm/arch/cpu.h>
-void mctl_set_timing_params(struct dram_para *para) +void mctl_set_timing_params(const struct dram_para *para) { struct sunxi_mctl_ctl_reg * const mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;

Dne sreda, 07. junij 2023 ob 02:07:42 CEST je Andre Przywara napisal(a):
There are quite some functions in the Allwinner H616 DRAM "driver", some of them actually change the parameters in the structure passed to them, but many are actually not. To increase the optimisation potential for the code, mark those functions that just read members of the passed dram_para struct as "const". This in itself does not decrease the code size, but lays the groundwork for future changes doing so.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

Currently there is one DRAM parameter struct for the Allwinner H616 DRAM "driver". It contains many fields that are compile time constants (set by Kconfig variables), though there are also some fields that are probed and changed over the runtime of the DRAM initialisation.
Because of this mixture, the compiler cannot properly optimise the code for size, as it does not consider constant propagation in its full potential.
Help the compiler out by splitting that structure into two: one that only contains values known at compile time, and another one where the values will actually change. The former can then be declared "const", which will let the compiler fold its values directly into the code using it.
We also add "const" tags for some new "struct dram_config" pointers, to further increase code optimisation. To help the compiler optimise the code further, the definition of the now "const struct dram_para" has to happen at a file-global level, so move that part out of sunxi_dram_init().
That results in quite some code savings (almost 2KB), and helps to keep the code small with the LPDDR3 support added later.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- .../include/asm/arch-sunxi/dram_sun50i_h616.h | 10 +- arch/arm/mach-sunxi/dram_sun50i_h616.c | 182 +++++++++--------- 2 files changed, 101 insertions(+), 91 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h index 34d19f71438..11774deded0 100644 --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h @@ -148,10 +148,6 @@ check_member(sunxi_mctl_ctl_reg, unk_0x4240, 0x4240); struct dram_para { u32 clk; enum sunxi_dram_type type; - u8 cols; - u8 rows; - u8 ranks; - u8 bus_full_width; u32 dx_odt; u32 dx_dri; u32 ca_dri; @@ -163,6 +159,12 @@ struct dram_para { u32 tpr12; };
+struct dram_config { + u8 cols; + u8 rows; + u8 ranks; + u8 bus_full_width; +};
static inline int ns_to_t(int nanoseconds) { diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index e0ee764cec7..4e988cebf59 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -141,15 +141,15 @@ static void mctl_sys_init(u32 clk_rate) writel(0x8000, &mctl_ctl->clken); }
-static void mctl_set_addrmap(const struct dram_para *para) +static void mctl_set_addrmap(const struct dram_config *config) { struct sunxi_mctl_ctl_reg * const mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; - u8 cols = para->cols; - u8 rows = para->rows; - u8 ranks = para->ranks; + u8 cols = config->cols; + u8 rows = config->rows; + u8 ranks = config->ranks;
- if (!para->bus_full_width) + if (!config->bus_full_width) cols -= 1;
/* Ranks */ @@ -281,7 +281,7 @@ static void mctl_phy_configure_odt(const struct dram_para *para) dmb(); }
-static bool mctl_phy_write_leveling(const struct dram_para *para) +static bool mctl_phy_write_leveling(const struct dram_config *config) { bool result = true; u32 val; @@ -292,7 +292,7 @@ static bool mctl_phy_write_leveling(const struct dram_para *para)
setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4);
- if (para->bus_full_width) + if (config->bus_full_width) val = 0xf; else val = 3; @@ -316,12 +316,12 @@ static bool mctl_phy_write_leveling(const struct dram_para *para)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0);
- if (para->ranks == 2) { + if (config->ranks == 2) { clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0, 0x40);
setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4);
- if (para->bus_full_width) + if (config->bus_full_width) val = 0xf; else val = 3; @@ -336,7 +336,7 @@ static bool mctl_phy_write_leveling(const struct dram_para *para) return result; }
-static bool mctl_phy_read_calibration(const struct dram_para *para) +static bool mctl_phy_read_calibration(const struct dram_config *config) { bool result = true; u32 val, tmp; @@ -345,7 +345,7 @@ static bool mctl_phy_read_calibration(const struct dram_para *para)
setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);
- if (para->bus_full_width) + if (config->bus_full_width) val = 0xf; else val = 3; @@ -361,7 +361,7 @@ static bool mctl_phy_read_calibration(const struct dram_para *para)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30);
- if (para->ranks == 2) { + if (config->ranks == 2) { clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x10);
setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); @@ -395,7 +395,7 @@ static bool mctl_phy_read_calibration(const struct dram_para *para) return result; }
-static bool mctl_phy_read_training(const struct dram_para *para) +static bool mctl_phy_read_training(const struct dram_config *config) { u32 val1, val2, *ptr1, *ptr2; bool result = true; @@ -414,7 +414,7 @@ static bool mctl_phy_read_training(const struct dram_para *para) if (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3) result = false;
- if (para->bus_full_width) { + if (config->bus_full_width) { mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc); if (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3) result = false; @@ -437,7 +437,7 @@ static bool mctl_phy_read_training(const struct dram_para *para) result = false; }
- if (para->bus_full_width) { + if (config->bus_full_width) { ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa98); ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa50); for (i = 0; i < 9; i++) { @@ -459,7 +459,7 @@ static bool mctl_phy_read_training(const struct dram_para *para)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3);
- if (para->ranks == 2) { + if (config->ranks == 2) { /* maybe last parameter should be 1? */ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2);
@@ -470,7 +470,7 @@ static bool mctl_phy_read_training(const struct dram_para *para) if (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3) result = false;
- if (para->bus_full_width) { + if (config->bus_full_width) { mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc); if (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3) result = false; @@ -484,7 +484,7 @@ static bool mctl_phy_read_training(const struct dram_para *para) return result; }
-static bool mctl_phy_write_training(const struct dram_para *para) +static bool mctl_phy_write_training(const struct dram_config *config) { u32 val1, val2, *ptr1, *ptr2; bool result = true; @@ -504,7 +504,7 @@ static bool mctl_phy_write_training(const struct dram_para *para) if (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc) result = false;
- if (para->bus_full_width) { + if (config->bus_full_width) { mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3); if (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc) result = false; @@ -527,7 +527,7 @@ static bool mctl_phy_write_training(const struct dram_para *para) result = false; }
- if (para->bus_full_width) { + if (config->bus_full_width) { ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xb38); ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xaf0); for (i = 0; i < 9; i++) { @@ -548,7 +548,7 @@ static bool mctl_phy_write_training(const struct dram_para *para)
clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60);
- if (para->ranks == 2) { + if (config->ranks == 2) { clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 4);
setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10); @@ -558,7 +558,7 @@ static bool mctl_phy_write_training(const struct dram_para *para) if (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc) result = false;
- if (para->bus_full_width) { + if (config->bus_full_width) { mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3); if (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc) result = false; @@ -773,7 +773,8 @@ static void mctl_phy_bit_delay_compensation(const struct dram_para *para) } }
-static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para) +static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para, + const struct dram_config *config) { u32 val, *ptr; int i; @@ -797,7 +798,7 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para) val = (para->tpr10 >> 7) & 0x1e; if (para->tpr2 & 1) { writel(val, SUNXI_DRAM_PHY0_BASE + 0x794); - if (para->ranks == 2) { + if (config->ranks == 2) { val = (para->tpr10 >> 11) & 0x1e; writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4); } @@ -809,7 +810,7 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para) } } else { writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4); - if (para->ranks == 2) { + if (config->ranks == 2) { val = (para->tpr10 >> 11) & 0x1e; writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c); } @@ -822,7 +823,8 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para) } }
-static bool mctl_phy_init(const struct dram_para *para) +static bool mctl_phy_init(const struct dram_para *para, + const struct dram_config *config) { struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; @@ -831,7 +833,7 @@ static bool mctl_phy_init(const struct dram_para *para) u32 val, val2, *ptr, mr0, mr2; int i;
- if (para->bus_full_width) + if (config->bus_full_width) val = 0xf; else val = 3; @@ -865,7 +867,7 @@ static bool mctl_phy_init(const struct dram_para *para) writel(phy_init[i], &ptr[i]);
if (para->tpr10 & TPR10_CA_BIT_DELAY) - mctl_phy_ca_bit_delay_compensation(para); + mctl_phy_ca_bit_delay_compensation(para, config);
writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x3dc); writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x45c); @@ -946,7 +948,7 @@ static bool mctl_phy_init(const struct dram_para *para)
if (para->tpr10 & TPR10_WRITE_LEVELING) { for (i = 0; i < 5; i++) - if (mctl_phy_write_leveling(para)) + if (mctl_phy_write_leveling(config)) break; if (i == 5) { debug("write leveling failed!\n"); @@ -956,7 +958,7 @@ static bool mctl_phy_init(const struct dram_para *para)
if (para->tpr10 & TPR10_READ_CALIBRATION) { for (i = 0; i < 5; i++) - if (mctl_phy_read_calibration(para)) + if (mctl_phy_read_calibration(config)) break; if (i == 5) { debug("read calibration failed!\n"); @@ -966,7 +968,7 @@ static bool mctl_phy_init(const struct dram_para *para)
if (para->tpr10 & TPR10_READ_TRAINING) { for (i = 0; i < 5; i++) - if (mctl_phy_read_training(para)) + if (mctl_phy_read_training(config)) break; if (i == 5) { debug("read training failed!\n"); @@ -976,7 +978,7 @@ static bool mctl_phy_init(const struct dram_para *para)
if (para->tpr10 & TPR10_WRITE_TRAINING) { for (i = 0; i < 5; i++) - if (mctl_phy_write_training(para)) + if (mctl_phy_write_training(config)) break; if (i == 5) { debug("write training failed!\n"); @@ -991,7 +993,8 @@ static bool mctl_phy_init(const struct dram_para *para) return true; }
-static bool mctl_ctrl_init(const struct dram_para *para) +static bool mctl_ctrl_init(const struct dram_para *para, + const struct dram_config *config) { struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; @@ -1010,15 +1013,15 @@ static bool mctl_ctrl_init(const struct dram_para *para)
setbits_le32(&mctl_com->unk_0x008, 0xff00);
- reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks); + reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(config->ranks); reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; - if (para->bus_full_width) + if (config->bus_full_width) reg_val |= MSTR_BUSWIDTH_FULL; else reg_val |= MSTR_BUSWIDTH_HALF; writel(BIT(31) | BIT(30) | reg_val, &mctl_ctl->mstr);
- if (para->ranks == 2) + if (config->ranks == 2) writel(0x0303, &mctl_ctl->odtmap); else writel(0x0201, &mctl_ctl->odtmap); @@ -1030,7 +1033,7 @@ static bool mctl_ctrl_init(const struct dram_para *para)
writel(BIT(31), &mctl_com->cr);
- mctl_set_addrmap(para); + mctl_set_addrmap(config);
mctl_set_timing_params(para);
@@ -1056,7 +1059,7 @@ static bool mctl_ctrl_init(const struct dram_para *para) /* this write seems to enable PHY MMIO region */ setbits_le32(&mctl_com->unk_0x500, BIT(24));
- if (!mctl_phy_init(para)) + if (!mctl_phy_init(para, config)) return false;
writel(0, &mctl_ctl->swctl); @@ -1073,18 +1076,20 @@ static bool mctl_ctrl_init(const struct dram_para *para) return true; }
-static bool mctl_core_init(const struct dram_para *para) +static bool mctl_core_init(const struct dram_para *para, + const struct dram_config *config) { mctl_sys_init(para->clk);
- return mctl_ctrl_init(para); + return mctl_ctrl_init(para, config); }
-static void mctl_auto_detect_rank_width(struct dram_para *para) +static void mctl_auto_detect_rank_width(const struct dram_para *para, + struct dram_config *config) { /* this is minimum size that it's supported */ - para->cols = 8; - para->rows = 13; + config->cols = 8; + config->rows = 13;
/* * Strategy here is to test most demanding combination first and least @@ -1095,94 +1100,97 @@ static void mctl_auto_detect_rank_width(struct dram_para *para) */
debug("testing 32-bit width, rank = 2\n"); - para->bus_full_width = 1; - para->ranks = 2; - if (mctl_core_init(para)) + config->bus_full_width = 1; + config->ranks = 2; + if (mctl_core_init(para, config)) return;
debug("testing 32-bit width, rank = 1\n"); - para->bus_full_width = 1; - para->ranks = 1; - if (mctl_core_init(para)) + config->bus_full_width = 1; + config->ranks = 1; + if (mctl_core_init(para, config)) return;
debug("testing 16-bit width, rank = 2\n"); - para->bus_full_width = 0; - para->ranks = 2; - if (mctl_core_init(para)) + config->bus_full_width = 0; + config->ranks = 2; + if (mctl_core_init(para, config)) return;
debug("testing 16-bit width, rank = 1\n"); - para->bus_full_width = 0; - para->ranks = 1; - if (mctl_core_init(para)) + config->bus_full_width = 0; + config->ranks = 1; + if (mctl_core_init(para, config)) return;
panic("This DRAM setup is currently not supported.\n"); }
-static void mctl_auto_detect_dram_size(struct dram_para *para) +static void mctl_auto_detect_dram_size(const struct dram_para *para, + struct dram_config *config) { /* detect row address bits */ - para->cols = 8; - para->rows = 18; - mctl_core_init(para); + config->cols = 8; + config->rows = 18; + mctl_core_init(para, config);
- for (para->rows = 13; para->rows < 18; para->rows++) { + for (config->rows = 13; config->rows < 18; config->rows++) { /* 8 banks, 8 bit per byte and 16/32 bit width */ - if (mctl_mem_matches((1 << (para->rows + para->cols + - 4 + para->bus_full_width)))) + if (mctl_mem_matches((1 << (config->rows + config->cols + + 4 + config->bus_full_width)))) break; }
/* detect column address bits */ - para->cols = 11; - mctl_core_init(para); + config->cols = 11; + mctl_core_init(para, config);
- for (para->cols = 8; para->cols < 11; para->cols++) { + for (config->cols = 8; config->cols < 11; config->cols++) { /* 8 bits per byte and 16/32 bit width */ - if (mctl_mem_matches(1 << (para->cols + 1 + - para->bus_full_width))) + if (mctl_mem_matches(1 << (config->cols + 1 + + config->bus_full_width))) break; } }
-static unsigned long mctl_calc_size(const struct dram_para *para) +static unsigned long mctl_calc_size(const struct dram_config *config) { - u8 width = para->bus_full_width ? 4 : 2; + u8 width = config->bus_full_width ? 4 : 2;
/* 8 banks */ - return (1ULL << (para->cols + para->rows + 3)) * width * para->ranks; + return (1ULL << (config->cols + config->rows + 3)) * width * config->ranks; }
+static const struct dram_para para = { + .clk = CONFIG_DRAM_CLK, + .type = SUNXI_DRAM_TYPE_DDR3, + .dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT, + .dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI, + .ca_dri = CONFIG_DRAM_SUN50I_H616_CA_DRI, + .odt_en = CONFIG_DRAM_SUN50I_H616_ODT_EN, + .tpr0 = CONFIG_DRAM_SUN50I_H616_TPR0, + .tpr2 = CONFIG_DRAM_SUN50I_H616_TPR2, + .tpr10 = CONFIG_DRAM_SUN50I_H616_TPR10, + .tpr11 = CONFIG_DRAM_SUN50I_H616_TPR11, + .tpr12 = CONFIG_DRAM_SUN50I_H616_TPR12, +}; + unsigned long sunxi_dram_init(void) { struct sunxi_prcm_reg *const prcm = (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; - struct dram_para para = { - .clk = CONFIG_DRAM_CLK, - .type = SUNXI_DRAM_TYPE_DDR3, - .dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT, - .dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI, - .ca_dri = CONFIG_DRAM_SUN50I_H616_CA_DRI, - .odt_en = CONFIG_DRAM_SUN50I_H616_ODT_EN, - .tpr0 = CONFIG_DRAM_SUN50I_H616_TPR0, - .tpr2 = CONFIG_DRAM_SUN50I_H616_TPR2, - .tpr10 = CONFIG_DRAM_SUN50I_H616_TPR10, - .tpr11 = CONFIG_DRAM_SUN50I_H616_TPR11, - .tpr12 = CONFIG_DRAM_SUN50I_H616_TPR12, - }; + struct dram_config config; unsigned long size;
setbits_le32(&prcm->res_cal_ctrl, BIT(8)); clrbits_le32(&prcm->ohms240, 0x3f);
- mctl_auto_detect_rank_width(¶); - mctl_auto_detect_dram_size(¶); + mctl_auto_detect_rank_width(¶, &config); + mctl_auto_detect_dram_size(¶, &config);
- mctl_core_init(¶); + mctl_core_init(¶, &config);
- size = mctl_calc_size(¶); + size = mctl_calc_size(&config);
mctl_set_master_priority();

Dne sreda, 07. junij 2023 ob 02:07:43 CEST je Andre Przywara napisal(a):
Currently there is one DRAM parameter struct for the Allwinner H616 DRAM "driver". It contains many fields that are compile time constants (set by Kconfig variables), though there are also some fields that are probed and changed over the runtime of the DRAM initialisation.
Because of this mixture, the compiler cannot properly optimise the code for size, as it does not consider constant propagation in its full potential.
Help the compiler out by splitting that structure into two: one that only contains values known at compile time, and another one where the values will actually change. The former can then be declared "const", which will let the compiler fold its values directly into the code using it.
We also add "const" tags for some new "struct dram_config" pointers, to further increase code optimisation. To help the compiler optimise the code further, the definition of the now "const struct dram_para" has to happen at a file-global level, so move that part out of sunxi_dram_init().
That results in quite some code savings (almost 2KB), and helps to keep the code small with the LPDDR3 support added later.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Nice trick. It could be used also in other DRAM drivers.
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

On Fri, 09 Jun 2023 22:26:21 +0200 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi,
Dne sreda, 07. junij 2023 ob 02:07:43 CEST je Andre Przywara napisal(a):
Currently there is one DRAM parameter struct for the Allwinner H616 DRAM "driver". It contains many fields that are compile time constants (set by Kconfig variables), though there are also some fields that are probed and changed over the runtime of the DRAM initialisation.
Because of this mixture, the compiler cannot properly optimise the code for size, as it does not consider constant propagation in its full potential.
Help the compiler out by splitting that structure into two: one that only contains values known at compile time, and another one where the values will actually change. The former can then be declared "const", which will let the compiler fold its values directly into the code using it.
We also add "const" tags for some new "struct dram_config" pointers, to further increase code optimisation. To help the compiler optimise the code further, the definition of the now "const struct dram_para" has to happen at a file-global level, so move that part out of sunxi_dram_init().
That results in quite some code savings (almost 2KB), and helps to keep the code small with the LPDDR3 support added later.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Nice trick. It could be used also in other DRAM drivers.
You bet ;-)
I have a patch for the H6 already. It doesn't save as much there (merely 500 bytes), I guess because we don't have as many build time parameters, but the H6 SPL is very big, so every byte saved there is a win. Will post this ASAP.
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Many thanks for that!
Cheers, Andre

From: iuncuim iuncuim@gmail.com
Allwinner H616 SoC supports several types of DRAM memory. To further integrate other types of memory, we need to add this delimitation. --- arch/arm/mach-sunxi/Kconfig | 10 +++++++++- arch/arm/mach-sunxi/dram_timings/Makefile | 3 +-- configs/orangepi_zero2_defconfig | 1 + configs/x96_mate_defconfig | 1 + 4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 6dcbb096f74..197d77ea658 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -442,7 +442,7 @@ config ARM_BOOT_HOOK_RMR This allows both the SPL and the U-Boot proper to be entered in either mode and switch to AArch64 if needed.
-if SUNXI_DRAM_DW || DRAM_SUN50I_H6 +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 config SUNXI_DRAM_DDR3 bool
@@ -487,6 +487,14 @@ config SUNXI_DRAM_H6_DDR3_1333 This option is the DDR3 timing used by the boot0 on H6 TV boxes which use a DDR3-1333 timing.
+config SUNXI_DRAM_H616_DDR3_1333 + bool "DDR3-1333 boot0 timings on the H616 DRAM controller" + select SUNXI_DRAM_DDR3 + depends on DRAM_SUN50I_H616 + ---help--- + This option is the DDR3 timing used by the boot0 on H616 TV boxes + which use a DDR3-1333 timing. + config SUNXI_DRAM_DDR2_V3S bool "DDR2 found in V3s chip" select SUNXI_DRAM_DDR2 diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 39a8756c297..4d78c04c9ae 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -3,5 +3,4 @@ obj-$(CONFIG_SUNXI_DRAM_LPDDR3_STOCK) += lpddr3_stock.o obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o -# currently only DDR3 is supported on H616 -obj-$(CONFIG_MACH_SUN50I_H616) += h616_ddr3_1333.o +obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o diff --git a/configs/orangepi_zero2_defconfig b/configs/orangepi_zero2_defconfig index 6cb942f511a..e38cc20ac72 100644 --- a/configs/orangepi_zero2_defconfig +++ b/configs/orangepi_zero2_defconfig @@ -7,6 +7,7 @@ CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e CONFIG_DRAM_SUN50I_H616_CA_DRI=0x0e0e CONFIG_DRAM_SUN50I_H616_TPR10=0xf83438 CONFIG_MACH_SUN50I_H616=y +CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_R_I2C_ENABLE=y CONFIG_SPL_SPI_SUNXI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/x96_mate_defconfig b/configs/x96_mate_defconfig index 122c1a99e32..c86cf43771c 100644 --- a/configs/x96_mate_defconfig +++ b/configs/x96_mate_defconfig @@ -10,6 +10,7 @@ CONFIG_DRAM_SUN50I_H616_TPR10=0x2f0007 CONFIG_DRAM_SUN50I_H616_TPR11=0xffffdddd CONFIG_DRAM_SUN50I_H616_TPR12=0xfedf7557 CONFIG_MACH_SUN50I_H616=y +CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_R_I2C_ENABLE=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y

Dne sreda, 07. junij 2023 ob 02:07:44 CEST je Andre Przywara napisal(a):
From: iuncuim iuncuim@gmail.com
Allwinner H616 SoC supports several types of DRAM memory. To further integrate other types of memory, we need to add this delimitation.
arch/arm/mach-sunxi/Kconfig | 10 +++++++++- arch/arm/mach-sunxi/dram_timings/Makefile | 3 +-- configs/orangepi_zero2_defconfig | 1 + configs/x96_mate_defconfig | 1 + 4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 6dcbb096f74..197d77ea658 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -442,7 +442,7 @@ config ARM_BOOT_HOOK_RMR This allows both the SPL and the U-Boot proper to be entered in either mode and switch to AArch64 if needed.
-if SUNXI_DRAM_DW || DRAM_SUN50I_H6 +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 config SUNXI_DRAM_DDR3 bool
@@ -487,6 +487,14 @@ config SUNXI_DRAM_H6_DDR3_1333 This option is the DDR3 timing used by the boot0 on H6 TV boxes which use a DDR3-1333 timing.
+config SUNXI_DRAM_H616_DDR3_1333
- bool "DDR3-1333 boot0 timings on the H616 DRAM controller"
- select SUNXI_DRAM_DDR3
- depends on DRAM_SUN50I_H616
- ---help---
- This option is the DDR3 timing used by the boot0 on H616 TV boxes
- which use a DDR3-1333 timing.
I'm always a bit unsure about help part. There is no need for "---", right? I also usually ident help block with extra two spaces. Do we have any rules for that?
Apart from that and SoB tag, it looks good.
Best regards, Jernej
config SUNXI_DRAM_DDR2_V3S bool "DDR2 found in V3s chip" select SUNXI_DRAM_DDR2 diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 39a8756c297..4d78c04c9ae 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -3,5 +3,4 @@ obj-$(CONFIG_SUNXI_DRAM_LPDDR3_STOCK) += lpddr3_stock.o obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o -# currently only DDR3 is supported on H616 -obj-$(CONFIG_MACH_SUN50I_H616) += h616_ddr3_1333.o +obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o diff --git a/configs/orangepi_zero2_defconfig b/configs/orangepi_zero2_defconfig index 6cb942f511a..e38cc20ac72 100644 --- a/configs/orangepi_zero2_defconfig +++ b/configs/orangepi_zero2_defconfig @@ -7,6 +7,7 @@ CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e CONFIG_DRAM_SUN50I_H616_CA_DRI=0x0e0e CONFIG_DRAM_SUN50I_H616_TPR10=0xf83438 CONFIG_MACH_SUN50I_H616=y +CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_R_I2C_ENABLE=y CONFIG_SPL_SPI_SUNXI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/x96_mate_defconfig b/configs/x96_mate_defconfig index 122c1a99e32..c86cf43771c 100644 --- a/configs/x96_mate_defconfig +++ b/configs/x96_mate_defconfig @@ -10,6 +10,7 @@ CONFIG_DRAM_SUN50I_H616_TPR10=0x2f0007 CONFIG_DRAM_SUN50I_H616_TPR11=0xffffdddd CONFIG_DRAM_SUN50I_H616_TPR12=0xfedf7557 CONFIG_MACH_SUN50I_H616=y +CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_R_I2C_ENABLE=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y

From: iuncuim iuncuim@gmail.com
The H616 SoC has support for several types of DRAM: DDR3, LPDDR3, DDR4 and LPDDR4. At the moment, the driver only supports DDR3 memory. Let's extend the driver to support the LPDDR3 memory. All "magic" values obtained from the boot0. --- arch/arm/mach-sunxi/Kconfig | 8 + arch/arm/mach-sunxi/dram_sun50i_h616.c | 193 +++++++++++++----- arch/arm/mach-sunxi/dram_timings/Makefile | 1 + .../arm/mach-sunxi/dram_timings/h616_lpddr3.c | 95 +++++++++ 4 files changed, 242 insertions(+), 55 deletions(-) create mode 100644 arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 197d77ea658..5ce82a955c6 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -487,6 +487,14 @@ config SUNXI_DRAM_H6_DDR3_1333 This option is the DDR3 timing used by the boot0 on H6 TV boxes which use a DDR3-1333 timing.
+config SUNXI_DRAM_H616_LPDDR3 + bool "LPDDR3 DRAM chips on the H616 DRAM controller" + select SUNXI_DRAM_LPDDR3 + depends on DRAM_SUN50I_H616 + ---help--- + This option is the LPDDR3 timing used by the stock boot0 by + Allwinner. + config SUNXI_DRAM_H616_DDR3_1333 bool "DDR3-1333 boot0 timings on the H616 DRAM controller" select SUNXI_DRAM_DDR3 diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 4e988cebf59..082746ea7f3 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -228,10 +228,17 @@ static void mctl_set_addrmap(const struct dram_config *config) }
static const u8 phy_init[] = { +#ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 0x07, 0x0b, 0x02, 0x16, 0x0d, 0x0e, 0x14, 0x19, 0x0a, 0x15, 0x03, 0x13, 0x04, 0x0c, 0x10, 0x06, 0x0f, 0x11, 0x1a, 0x01, 0x12, 0x17, 0x00, 0x08, 0x09, 0x05, 0x18 +#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3) + 0x18, 0x06, 0x00, 0x05, 0x04, 0x03, 0x09, 0x02, + 0x08, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07, + 0x17, 0x19, 0x1a +#endif };
static void mctl_phy_configure_odt(const struct dram_para *para) @@ -263,19 +270,31 @@ static void mctl_phy_configure_odt(const struct dram_para *para) writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x34c);
val = para->dx_odt & 0x1f; - writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x380); + else + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380); writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x384);
val = (para->dx_odt >> 8) & 0x1f; - writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x3c0); + else + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0); writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c4);
val = (para->dx_odt >> 16) & 0x1f; - writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x400); + else + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400); writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x404);
val = (para->dx_odt >> 24) & 0x1f; - writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x440); + else + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440); writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x444);
dmb(); @@ -794,31 +813,47 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para, writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e0); writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f4);
- /* following configuration is DDR3 specific */ - val = (para->tpr10 >> 7) & 0x1e; - if (para->tpr2 & 1) { - writel(val, SUNXI_DRAM_PHY0_BASE + 0x794); - if (config->ranks == 2) { - val = (para->tpr10 >> 11) & 0x1e; - writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4); - } - if (para->tpr0 & BIT(31)) { - val = (para->tpr0 << 1) & 0x3e; - writel(val, SUNXI_DRAM_PHY0_BASE + 0x790); - writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8); - writel(val, SUNXI_DRAM_PHY0_BASE + 0x7cc); - } - } else { - writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4); - if (config->ranks == 2) { - val = (para->tpr10 >> 11) & 0x1e; - writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c); + if (para->type == SUNXI_DRAM_TYPE_DDR3) { + val = (para->tpr10 >> 7) & 0x1e; + if (para->tpr2 & 1) { + writel(val, SUNXI_DRAM_PHY0_BASE + 0x794); + if (config->ranks == 2) { + val = (para->tpr10 >> 11) & 0x1e; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4); + } + if (para->tpr0 & BIT(31)) { + val = (para->tpr0 << 1) & 0x3e; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x790); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7cc); + } + } else { + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4); + if (config->ranks == 2) { + val = (para->tpr10 >> 11) & 0x1e; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c); + } + if (para->tpr0 & BIT(31)) { + val = (para->tpr0 << 1) & 0x3e; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x78c); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a4); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8); + } } - if (para->tpr0 & BIT(31)) { - val = (para->tpr0 << 1) & 0x3e; - writel(val, SUNXI_DRAM_PHY0_BASE + 0x78c); - writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a4); - writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8); + } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + val = (para->tpr10 >> 7) & 0x1e; + if (para->tpr2 & 1) { + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a0); + if (config->ranks == 2) { + val = (para->tpr10 >> 11) & 0x1e; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c); + } + } else { + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e8); + if (config->ranks == 2) { + val = (para->tpr10 >> 11) & 0x1e; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f8); + } } } } @@ -840,11 +875,22 @@ static bool mctl_phy_init(const struct dram_para *para, clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);
if (para->tpr2 & 0x100) { - val = 9; - val2 = 7; + if (para->type == SUNXI_DRAM_TYPE_DDR3) { + val = 9; + val2 = 7; + } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + // TODO: untested setup: use some values for now + val = 14; + val2 = 8; + } } else { - val = 13; - val2 = 9; + if (para->type == SUNXI_DRAM_TYPE_DDR3) { + val = 13; + val2 = 9; + } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + val = 14; + val2 = 8; + } }
writel(val, SUNXI_DRAM_PHY0_BASE + 0x14); @@ -869,12 +915,20 @@ static bool mctl_phy_init(const struct dram_para *para, if (para->tpr10 & TPR10_CA_BIT_DELAY) mctl_phy_ca_bit_delay_compensation(para, config);
- writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x3dc); - writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x45c); + if (para->type == SUNXI_DRAM_TYPE_DDR3) + val = 0x80; + else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + val = 0xc0; + writel(val, SUNXI_DRAM_PHY0_BASE + 0x3dc); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x45c);
mctl_phy_configure_odt(para);
- clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa); + if (para->type == SUNXI_DRAM_TYPE_DDR3) { + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa); + } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xb); + }
if (para->clk <= 672) writel(0xf, SUNXI_DRAM_PHY0_BASE + 0x20); @@ -924,21 +978,39 @@ static bool mctl_phy_init(const struct dram_para *para, mr2 = 0x20; }
- writel(mr0, &mctl_ctl->mrctrl1); - writel(0x80000030, &mctl_ctl->mrctrl0); - mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); - - writel(4, &mctl_ctl->mrctrl1); - writel(0x80001030, &mctl_ctl->mrctrl0); - mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); - - writel(mr2, &mctl_ctl->mrctrl1); - writel(0x80002030, &mctl_ctl->mrctrl0); - mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); - - writel(0, &mctl_ctl->mrctrl1); - writel(0x80003030, &mctl_ctl->mrctrl0); - mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + if (para->type == SUNXI_DRAM_TYPE_DDR3) { + writel(mr0, &mctl_ctl->mrctrl1); + writel(0x80000030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(4, &mctl_ctl->mrctrl1); + writel(0x80001030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(mr2, &mctl_ctl->mrctrl1); + writel(0x80002030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(0, &mctl_ctl->mrctrl1); + writel(0x80003030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + writel(mr0, &mctl_ctl->mrctrl1); + writel(0x800000f0, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(4, &mctl_ctl->mrctrl1); + writel(0x800000f0, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(mr2, &mctl_ctl->mrctrl1); + writel(0x800000f0, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(0x301, &mctl_ctl->mrctrl1); + writel(0x800000f0, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + }
writel(0, SUNXI_DRAM_PHY0_BASE + 0x54);
@@ -1014,7 +1086,10 @@ static bool mctl_ctrl_init(const struct dram_para *para, setbits_le32(&mctl_com->unk_0x008, 0xff00);
reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(config->ranks); - reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; + else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + reg_val |= MSTR_DEVICETYPE_LPDDR3; if (config->bus_full_width) reg_val |= MSTR_BUSWIDTH_FULL; else @@ -1026,10 +1101,14 @@ static bool mctl_ctrl_init(const struct dram_para *para, else writel(0x0201, &mctl_ctl->odtmap);
- writel(0x06000400, &mctl_ctl->odtcfg); - writel(0x06000400, &mctl_ctl->unk_0x2240); - writel(0x06000400, &mctl_ctl->unk_0x3240); - writel(0x06000400, &mctl_ctl->unk_0x4240); + if (para->type == SUNXI_DRAM_TYPE_DDR3) + reg_val = 0x06000400; + else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + reg_val = 0x09020400; + writel(reg_val, &mctl_ctl->odtcfg); + writel(reg_val, &mctl_ctl->unk_0x2240); + writel(reg_val, &mctl_ctl->unk_0x3240); + writel(reg_val, &mctl_ctl->unk_0x4240);
writel(BIT(31), &mctl_com->cr);
@@ -1163,7 +1242,11 @@ static unsigned long mctl_calc_size(const struct dram_config *config)
static const struct dram_para para = { .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 .type = SUNXI_DRAM_TYPE_DDR3, +#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3) + .type = SUNXI_DRAM_TYPE_LPDDR3, +#endif .dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT, .dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI, .ca_dri = CONFIG_DRAM_SUN50I_H616_CA_DRI, diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 4d78c04c9ae..8bfd99448a8 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o +obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o diff --git a/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c b/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c new file mode 100644 index 00000000000..b6d6a687468 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c @@ -0,0 +1,95 @@ +/* + * sun50i H616 LPDDR3 timings, as programmed by Allwinner's boot0 + * + * The chips are probably able to be driven by a faster clock, but boot0 + * uses a more conservative timing (as usual). + * + * (C) Copyright 2020 Jernej Skrabec jernej.skrabec@siol.net + * Based on H6 DDR3 timings: + * (C) Copyright 2018,2019 Arm Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +void mctl_set_timing_params(const struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 tccd = 2; + u8 tfaw = ns_to_t(50); + u8 trrd = max(ns_to_t(6), 4); + u8 trcd = ns_to_t(24); + u8 trc = ns_to_t(70); + u8 txp = max(ns_to_t(8), 3); + u8 trtp = max(ns_to_t(8), 2); + u8 trp = ns_to_t(27); + u8 tras = ns_to_t(41); + u16 trefi = ns_to_t(7800) / 64; + u16 trfc = ns_to_t(210); + u16 txsr = 88; + + u8 tmrw = 5; + u8 tmrd = 5; + u8 tmod = max(ns_to_t(15), 12); + u8 tcke = max(ns_to_t(6), 3); + u8 tcksrx = max(ns_to_t(12), 4); + u8 tcksre = max(ns_to_t(12), 4); + u8 tckesr = tcke + 2; + u8 trasmax = (para->clk / 2) / 16; + u8 txs = ns_to_t(360) / 32; + u8 txsdll = 16; + u8 txsabort = 4; + u8 txsfast = 4; + u8 tcl = 7; + u8 tcwl = 4; + u8 t_rdata_en = 12; + u8 t_wr_lat = 6; + + u8 twtp = 16; + u8 twr2rd = trtp + 9; + u8 trd2wr = 13; + + /* DRAM timing grabbed from tvbox with LPDDR3 memory */ + 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]); + + writel(0x4f0112, &mctl_ctl->init[0]); + writel(0x420000, &mctl_ctl->init[1]); + writel(0xd05, &mctl_ctl->init[2]); + writel(0x83001c, &mctl_ctl->init[3]); + writel(0x00010000, &mctl_ctl->init[4]); + + writel(0, &mctl_ctl->dfimisc); + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + + /* Configure DFI timing */ + writel(t_wr_lat | 0x2000000 | (t_rdata_en << 16) | 0x808000, + &mctl_ctl->dfitmg0); + writel(0x100202, &mctl_ctl->dfitmg1); + + /* set refresh timing */ + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); +}

Dne sreda, 07. junij 2023 ob 02:07:45 CEST je Andre Przywara napisal(a):
From: iuncuim iuncuim@gmail.com
The H616 SoC has support for several types of DRAM: DDR3, LPDDR3, DDR4 and LPDDR4. At the moment, the driver only supports DDR3 memory. Let's extend the driver to support the LPDDR3 memory. All "magic" values obtained from the boot0.
arch/arm/mach-sunxi/Kconfig | 8 + arch/arm/mach-sunxi/dram_sun50i_h616.c | 193 +++++++++++++----- arch/arm/mach-sunxi/dram_timings/Makefile | 1 + .../arm/mach-sunxi/dram_timings/h616_lpddr3.c | 95 +++++++++ 4 files changed, 242 insertions(+), 55 deletions(-) create mode 100644 arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 197d77ea658..5ce82a955c6 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -487,6 +487,14 @@ config SUNXI_DRAM_H6_DDR3_1333 This option is the DDR3 timing used by the boot0 on H6 TV boxes which use a DDR3-1333 timing.
+config SUNXI_DRAM_H616_LPDDR3
- bool "LPDDR3 DRAM chips on the H616 DRAM controller"
- select SUNXI_DRAM_LPDDR3
- depends on DRAM_SUN50I_H616
- ---help---
- This option is the LPDDR3 timing used by the stock boot0 by
- Allwinner.
config SUNXI_DRAM_H616_DDR3_1333 bool "DDR3-1333 boot0 timings on the H616 DRAM controller" select SUNXI_DRAM_DDR3 diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 4e988cebf59..082746ea7f3 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -228,10 +228,17 @@ static void mctl_set_addrmap(const struct dram_config *config) }
static const u8 phy_init[] = { +#ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 0x07, 0x0b, 0x02, 0x16, 0x0d, 0x0e, 0x14, 0x19, 0x0a, 0x15, 0x03, 0x13, 0x04, 0x0c, 0x10, 0x06, 0x0f, 0x11, 0x1a, 0x01, 0x12, 0x17, 0x00, 0x08, 0x09, 0x05, 0x18 +#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3)
- 0x18, 0x06, 0x00, 0x05, 0x04, 0x03, 0x09, 0x02,
- 0x08, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07,
- 0x17, 0x19, 0x1a
+#endif };
static void mctl_phy_configure_odt(const struct dram_para *para) @@ -263,19 +270,31 @@ static void mctl_phy_configure_odt(const struct dram_para *para) writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x34c);
val = para->dx_odt & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x380);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x384);
val = (para->dx_odt >> 8) & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x3c0);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c4);
val = (para->dx_odt >> 16) & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x400);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x404);
val = (para->dx_odt >> 24) & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440);
- if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x440);
- else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x444);writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440);
Above dx_odt oriented changes are not needed. They depend on Kconfig symbol, which can simply be set to 0.
I didn't do detailed check, but if it works, it should be ok. Were calculated values here compared to vendor driver?
Best regards, Jernej
dmb(); @@ -794,31 +813,47 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para, writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e0); writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f4);
- /* following configuration is DDR3 specific */
- val = (para->tpr10 >> 7) & 0x1e;
- if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4);
}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x790);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7cc);
}
- } else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c);
- if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = (para->tpr10 >> 7) & 0x1e;
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4);
}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x790);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7cc);
}
} else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c);
}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x78c);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a4);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
}}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x78c);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a4);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
- } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
val = (para->tpr10 >> 7) & 0x1e;
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a0);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c);
}
} else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e8);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f8);
} }}
} @@ -840,11 +875,22 @@ static bool mctl_phy_init(const struct dram_para *para, clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);
if (para->tpr2 & 0x100) {
val = 9;
val2 = 7;
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = 9;
val2 = 7;
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
// TODO: untested setup: use some values for now
I'm not fan of such todos.
val = 14;
val2 = 8;
} else {}
val = 13;
val2 = 9;
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = 13;
val2 = 9;
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
val = 14;
val2 = 8;
}
}
writel(val, SUNXI_DRAM_PHY0_BASE + 0x14);
@@ -869,12 +915,20 @@ static bool mctl_phy_init(const struct dram_para *para, if (para->tpr10 & TPR10_CA_BIT_DELAY) mctl_phy_ca_bit_delay_compensation(para, config);
- writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x3dc);
- writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x45c);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
val = 0x80;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
val = 0xc0;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x3dc);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x45c);
mctl_phy_configure_odt(para);
- clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa);
- if (para->type == SUNXI_DRAM_TYPE_DDR3) {
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa);
- } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xb);
- }
To be consistent code style wise, above block could be rewritten to set val variable to either 0xa or 0xb and set block after that.
if (para->clk <= 672) writel(0xf, SUNXI_DRAM_PHY0_BASE + 0x20); @@ -924,21 +978,39 @@ static bool mctl_phy_init(const struct dram_para *para, mr2 = 0x20; }
- writel(mr0, &mctl_ctl->mrctrl1);
- writel(0x80000030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
- writel(4, &mctl_ctl->mrctrl1);
- writel(0x80001030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
- writel(mr2, &mctl_ctl->mrctrl1);
- writel(0x80002030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
- writel(0, &mctl_ctl->mrctrl1);
- writel(0x80003030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
writel(mr0, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(4, &mctl_ctl->mrctrl1);
writel(0x80001030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(mr2, &mctl_ctl->mrctrl1);
writel(0x80002030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0, &mctl_ctl->mrctrl1);
writel(0x80003030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
writel(mr0, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(4, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(mr2, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x301, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
}
writel(0, SUNXI_DRAM_PHY0_BASE + 0x54);
@@ -1014,7 +1086,10 @@ static bool mctl_ctrl_init(const struct dram_para *para, setbits_le32(&mctl_com->unk_0x008, 0xff00);
reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(config->ranks);
- reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
- if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
- else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
if (config->bus_full_width) reg_val |= MSTR_BUSWIDTH_FULL; elsereg_val |= MSTR_DEVICETYPE_LPDDR3;
@@ -1026,10 +1101,14 @@ static bool mctl_ctrl_init(const struct dram_para *para, else writel(0x0201, &mctl_ctl->odtmap);
- writel(0x06000400, &mctl_ctl->odtcfg);
- writel(0x06000400, &mctl_ctl->unk_0x2240);
- writel(0x06000400, &mctl_ctl->unk_0x3240);
- writel(0x06000400, &mctl_ctl->unk_0x4240);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val = 0x06000400;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
reg_val = 0x09020400;
writel(reg_val, &mctl_ctl->odtcfg);
writel(reg_val, &mctl_ctl->unk_0x2240);
writel(reg_val, &mctl_ctl->unk_0x3240);
writel(reg_val, &mctl_ctl->unk_0x4240);
writel(BIT(31), &mctl_com->cr);
@@ -1163,7 +1242,11 @@ static unsigned long mctl_calc_size(const struct dram_config *config)
static const struct dram_para para = { .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 .type = SUNXI_DRAM_TYPE_DDR3, +#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3)
- .type = SUNXI_DRAM_TYPE_LPDDR3,
+#endif .dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT, .dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI, .ca_dri = CONFIG_DRAM_SUN50I_H616_CA_DRI, diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 4d78c04c9ae..8bfd99448a8 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o +obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o diff --git a/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c b/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c new file mode 100644 index 00000000000..b6d6a687468 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c @@ -0,0 +1,95 @@ +/*
- sun50i H616 LPDDR3 timings, as programmed by Allwinner's boot0
- The chips are probably able to be driven by a faster clock, but boot0
- uses a more conservative timing (as usual).
- (C) Copyright 2020 Jernej Skrabec jernej.skrabec@siol.net
- Based on H6 DDR3 timings:
- (C) Copyright 2018,2019 Arm Ltd.
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h>
+void mctl_set_timing_params(const struct dram_para *para) +{
- struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
- u8 tccd = 2;
- u8 tfaw = ns_to_t(50);
- u8 trrd = max(ns_to_t(6), 4);
- u8 trcd = ns_to_t(24);
- u8 trc = ns_to_t(70);
- u8 txp = max(ns_to_t(8), 3);
- u8 trtp = max(ns_to_t(8), 2);
- u8 trp = ns_to_t(27);
- u8 tras = ns_to_t(41);
- u16 trefi = ns_to_t(7800) / 64;
- u16 trfc = ns_to_t(210);
- u16 txsr = 88;
- u8 tmrw = 5;
- u8 tmrd = 5;
- u8 tmod = max(ns_to_t(15), 12);
- u8 tcke = max(ns_to_t(6), 3);
- u8 tcksrx = max(ns_to_t(12), 4);
- u8 tcksre = max(ns_to_t(12), 4);
- u8 tckesr = tcke + 2;
- u8 trasmax = (para->clk / 2) / 16;
- u8 txs = ns_to_t(360) / 32;
- u8 txsdll = 16;
- u8 txsabort = 4;
- u8 txsfast = 4;
- u8 tcl = 7;
- u8 tcwl = 4;
- u8 t_rdata_en = 12;
- u8 t_wr_lat = 6;
- u8 twtp = 16;
- u8 twr2rd = trtp + 9;
- u8 trd2wr = 13;
- /* DRAM timing grabbed from tvbox with LPDDR3 memory */
- 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]);
- writel(0x4f0112, &mctl_ctl->init[0]);
- writel(0x420000, &mctl_ctl->init[1]);
- writel(0xd05, &mctl_ctl->init[2]);
- writel(0x83001c, &mctl_ctl->init[3]);
- writel(0x00010000, &mctl_ctl->init[4]);
- writel(0, &mctl_ctl->dfimisc);
- clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
- /* Configure DFI timing */
- writel(t_wr_lat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
- writel(0x100202, &mctl_ctl->dfitmg1);
- /* set refresh timing */
- writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}

On Fri, 09 Jun 2023 22:38:38 +0200 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi,
Dne sreda, 07. junij 2023 ob 02:07:45 CEST je Andre Przywara napisal(a):
From: iuncuim iuncuim@gmail.com
The H616 SoC has support for several types of DRAM: DDR3, LPDDR3, DDR4 and LPDDR4. At the moment, the driver only supports DDR3 memory. Let's extend the driver to support the LPDDR3 memory. All "magic" values obtained from the boot0.
arch/arm/mach-sunxi/Kconfig | 8 + arch/arm/mach-sunxi/dram_sun50i_h616.c | 193 +++++++++++++----- arch/arm/mach-sunxi/dram_timings/Makefile | 1 + .../arm/mach-sunxi/dram_timings/h616_lpddr3.c | 95 +++++++++ 4 files changed, 242 insertions(+), 55 deletions(-) create mode 100644 arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 197d77ea658..5ce82a955c6 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -487,6 +487,14 @@ config SUNXI_DRAM_H6_DDR3_1333 This option is the DDR3 timing used by the boot0 on H6 TV boxes which use a DDR3-1333 timing.
+config SUNXI_DRAM_H616_LPDDR3
- bool "LPDDR3 DRAM chips on the H616 DRAM controller"
- select SUNXI_DRAM_LPDDR3
- depends on DRAM_SUN50I_H616
- ---help---
- This option is the LPDDR3 timing used by the stock boot0 by
- Allwinner.
config SUNXI_DRAM_H616_DDR3_1333 bool "DDR3-1333 boot0 timings on the H616 DRAM controller" select SUNXI_DRAM_DDR3 diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 4e988cebf59..082746ea7f3 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -228,10 +228,17 @@ static void mctl_set_addrmap(const struct dram_config *config) }
static const u8 phy_init[] = { +#ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 0x07, 0x0b, 0x02, 0x16, 0x0d, 0x0e, 0x14, 0x19, 0x0a, 0x15, 0x03, 0x13, 0x04, 0x0c, 0x10, 0x06, 0x0f, 0x11, 0x1a, 0x01, 0x12, 0x17, 0x00, 0x08, 0x09, 0x05, 0x18 +#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3)
- 0x18, 0x06, 0x00, 0x05, 0x04, 0x03, 0x09, 0x02,
- 0x08, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07,
- 0x17, 0x19, 0x1a
+#endif };
static void mctl_phy_configure_odt(const struct dram_para *para) @@ -263,19 +270,31 @@ static void mctl_phy_configure_odt(const struct dram_para *para) writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x34c);
val = para->dx_odt & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x380);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x380);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x384);
val = (para->dx_odt >> 8) & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x3c0);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c0);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3c4);
val = (para->dx_odt >> 16) & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x400);
else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x400);
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x404);
val = (para->dx_odt >> 24) & 0x1f;
- writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440);
- if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x440);
- else
writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x444);writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x440);
Above dx_odt oriented changes are not needed. They depend on Kconfig symbol, which can simply be set to 0.
Ha, I thought so as well, but if you look closely, it's only *one* of the two writes which turn to zero, the other register stays at the Kconfig value. Annoying, I know ;-) I couldn't think of a shorter or even cleaner way to express this, really, apart from a macro maybe, so I guess we have to live with it.
Cheers, Andre
I didn't do detailed check, but if it works, it should be ok. Were calculated values here compared to vendor driver?
Best regards, Jernej
dmb(); @@ -794,31 +813,47 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para, writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e0); writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f4);
- /* following configuration is DDR3 specific */
- val = (para->tpr10 >> 7) & 0x1e;
- if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4);
}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x790);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7cc);
}
- } else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c);
- if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = (para->tpr10 >> 7) & 0x1e;
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e4);
}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x790);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7cc);
}
} else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7d4);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c);
}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x78c);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a4);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
}}
if (para->tpr0 & BIT(31)) {
val = (para->tpr0 << 1) & 0x3e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x78c);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a4);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
- } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
val = (para->tpr10 >> 7) & 0x1e;
if (para->tpr2 & 1) {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a0);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x79c);
}
} else {
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e8);
if (config->ranks == 2) {
val = (para->tpr10 >> 11) & 0x1e;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f8);
} }}
} @@ -840,11 +875,22 @@ static bool mctl_phy_init(const struct dram_para *para, clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);
if (para->tpr2 & 0x100) {
val = 9;
val2 = 7;
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = 9;
val2 = 7;
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
// TODO: untested setup: use some values for now
I'm not fan of such todos.
val = 14;
val2 = 8;
} else {}
val = 13;
val2 = 9;
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
val = 13;
val2 = 9;
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
val = 14;
val2 = 8;
}
}
writel(val, SUNXI_DRAM_PHY0_BASE + 0x14);
@@ -869,12 +915,20 @@ static bool mctl_phy_init(const struct dram_para *para, if (para->tpr10 & TPR10_CA_BIT_DELAY) mctl_phy_ca_bit_delay_compensation(para, config);
- writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x3dc);
- writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x45c);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
val = 0x80;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
val = 0xc0;
writel(val, SUNXI_DRAM_PHY0_BASE + 0x3dc);
writel(val, SUNXI_DRAM_PHY0_BASE + 0x45c);
mctl_phy_configure_odt(para);
- clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa);
- if (para->type == SUNXI_DRAM_TYPE_DDR3) {
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa);
- } else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xb);
- }
To be consistent code style wise, above block could be rewritten to set val variable to either 0xa or 0xb and set block after that.
if (para->clk <= 672) writel(0xf, SUNXI_DRAM_PHY0_BASE + 0x20); @@ -924,21 +978,39 @@ static bool mctl_phy_init(const struct dram_para *para, mr2 = 0x20; }
- writel(mr0, &mctl_ctl->mrctrl1);
- writel(0x80000030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
- writel(4, &mctl_ctl->mrctrl1);
- writel(0x80001030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
- writel(mr2, &mctl_ctl->mrctrl1);
- writel(0x80002030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
- writel(0, &mctl_ctl->mrctrl1);
- writel(0x80003030, &mctl_ctl->mrctrl0);
- mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
if (para->type == SUNXI_DRAM_TYPE_DDR3) {
writel(mr0, &mctl_ctl->mrctrl1);
writel(0x80000030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(4, &mctl_ctl->mrctrl1);
writel(0x80001030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(mr2, &mctl_ctl->mrctrl1);
writel(0x80002030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0, &mctl_ctl->mrctrl1);
writel(0x80003030, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
writel(mr0, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(4, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(mr2, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
writel(0x301, &mctl_ctl->mrctrl1);
writel(0x800000f0, &mctl_ctl->mrctrl0);
mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
}
writel(0, SUNXI_DRAM_PHY0_BASE + 0x54);
@@ -1014,7 +1086,10 @@ static bool mctl_ctrl_init(const struct dram_para *para, setbits_le32(&mctl_com->unk_0x008, 0xff00);
reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(config->ranks);
- reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
- if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
- else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
if (config->bus_full_width) reg_val |= MSTR_BUSWIDTH_FULL; elsereg_val |= MSTR_DEVICETYPE_LPDDR3;
@@ -1026,10 +1101,14 @@ static bool mctl_ctrl_init(const struct dram_para *para, else writel(0x0201, &mctl_ctl->odtmap);
- writel(0x06000400, &mctl_ctl->odtcfg);
- writel(0x06000400, &mctl_ctl->unk_0x2240);
- writel(0x06000400, &mctl_ctl->unk_0x3240);
- writel(0x06000400, &mctl_ctl->unk_0x4240);
if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val = 0x06000400;
else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
reg_val = 0x09020400;
writel(reg_val, &mctl_ctl->odtcfg);
writel(reg_val, &mctl_ctl->unk_0x2240);
writel(reg_val, &mctl_ctl->unk_0x3240);
writel(reg_val, &mctl_ctl->unk_0x4240);
writel(BIT(31), &mctl_com->cr);
@@ -1163,7 +1242,11 @@ static unsigned long mctl_calc_size(const struct dram_config *config)
static const struct dram_para para = { .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 .type = SUNXI_DRAM_TYPE_DDR3, +#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3)
- .type = SUNXI_DRAM_TYPE_LPDDR3,
+#endif .dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT, .dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI, .ca_dri = CONFIG_DRAM_SUN50I_H616_CA_DRI, diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 4d78c04c9ae..8bfd99448a8 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o +obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o diff --git a/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c b/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c new file mode 100644 index 00000000000..b6d6a687468 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/h616_lpddr3.c @@ -0,0 +1,95 @@ +/*
- sun50i H616 LPDDR3 timings, as programmed by Allwinner's boot0
- The chips are probably able to be driven by a faster clock, but boot0
- uses a more conservative timing (as usual).
- (C) Copyright 2020 Jernej Skrabec jernej.skrabec@siol.net
- Based on H6 DDR3 timings:
- (C) Copyright 2018,2019 Arm Ltd.
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h>
+void mctl_set_timing_params(const struct dram_para *para) +{
- struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
- u8 tccd = 2;
- u8 tfaw = ns_to_t(50);
- u8 trrd = max(ns_to_t(6), 4);
- u8 trcd = ns_to_t(24);
- u8 trc = ns_to_t(70);
- u8 txp = max(ns_to_t(8), 3);
- u8 trtp = max(ns_to_t(8), 2);
- u8 trp = ns_to_t(27);
- u8 tras = ns_to_t(41);
- u16 trefi = ns_to_t(7800) / 64;
- u16 trfc = ns_to_t(210);
- u16 txsr = 88;
- u8 tmrw = 5;
- u8 tmrd = 5;
- u8 tmod = max(ns_to_t(15), 12);
- u8 tcke = max(ns_to_t(6), 3);
- u8 tcksrx = max(ns_to_t(12), 4);
- u8 tcksre = max(ns_to_t(12), 4);
- u8 tckesr = tcke + 2;
- u8 trasmax = (para->clk / 2) / 16;
- u8 txs = ns_to_t(360) / 32;
- u8 txsdll = 16;
- u8 txsabort = 4;
- u8 txsfast = 4;
- u8 tcl = 7;
- u8 tcwl = 4;
- u8 t_rdata_en = 12;
- u8 t_wr_lat = 6;
- u8 twtp = 16;
- u8 twr2rd = trtp + 9;
- u8 trd2wr = 13;
- /* DRAM timing grabbed from tvbox with LPDDR3 memory */
- 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]);
- writel(0x4f0112, &mctl_ctl->init[0]);
- writel(0x420000, &mctl_ctl->init[1]);
- writel(0xd05, &mctl_ctl->init[2]);
- writel(0x83001c, &mctl_ctl->init[3]);
- writel(0x00010000, &mctl_ctl->init[4]);
- writel(0, &mctl_ctl->dfimisc);
- clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
- /* Configure DFI timing */
- writel(t_wr_lat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
- writel(0x100202, &mctl_ctl->dfitmg1);
- /* set refresh timing */
- writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}
participants (2)
-
Andre Przywara
-
Jernej Škrabec