[U-Boot] [PATCH v2 0/7] sunxi: Add H6 DDR3 DRAM support

An updated version, with minor changes. I realised that my comments about JEDEC values were still based on the DDR3-1600 speed bin, so I updated those to match the DDR3-1333 values. I also used the actual JEDEC recommended formulas where that didn't change the actual value written. For more details see the Changelog below. ----------------
So far the SPL DRAM driver for the Allwinner H6 SoC only supports LPDDR3 DRAM chips, which are used on most single board computers with this SoC. There are some TV boxes with the H6 out now, but most of them are using DDR3 DRAM instead of LPDDR3.
This series extends the existing H6 DRAM driver to cover DDR3 DRAMs as well. The information used in these patches is from: - register dumps after Allwinner's boot0 (libdram) has initialised the DRAM - some disassembly of the libdram library - timing parameters as found in the boot0 binary - comparison with Xilinx ZynqMP DRAM controller documentation
The box I played with (Eachlink H6 Mini) has 3GB of DDR3-1600 chips and runs at 840 MHz, however I couldn't get it to work with these parameters. Instead Jernej suggested to use a lower clock and adjust some timing parameters, which made it work for me as well.
Many thanks to Jernej Skrabec for his help, also to others who helped with testing and experiments.
The first two patches contain some fixes for the existing driver. Patch 3 moves the existing LPDDR3 timing parameters into a separate file, patch 5 introduces the respective DDR3 timings, patch 6 adds some generic delay lines values. Patch 4 enhances the DRAM controller driver to program DDR3 specific registers as well and use different settings on other registers. The final patch introduces support for the Eachlink H6 Mini TV box, with the usual device tree and defconfig file.
Please have a look and comment!
Cheers, Andre.
Changelog v1..v2: - name DDR2T mode bit - explain bonding ID bit - generalise message about non-supported DRAM types - update comments about JEDEC values to match DDR3-1333 timings - use recommended max(ns, cycles) timing formulas where appropriate - add MAINTAINERS entry for the Eachlink H6 Mini box
Andre Przywara (6): sunxi: H6: DRAM: avoid memcpy() on MMIO registers sunxi: H6: DRAM: follow recommended PHY init algorithm sunxi: H6: move LPDDR3 timing definition into separate file sunxi: H6: Add DDR3 support to DRAM controller driver sunxi: H6: Add DDR3-1333 timings sunxi: H6: Add basic Eachlink H6 Mini support
Jernej Skrabec (1): sunxi: H6: Add DDR3 DRAM delay values
arch/arm/dts/Makefile | 1 + arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts | 116 ++++++++ .../include/asm/arch-sunxi/dram_sun50i_h6.h | 35 +++ arch/arm/mach-sunxi/Kconfig | 18 +- arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/dram_sun50i_h6.c | 251 ++++++------------ arch/arm/mach-sunxi/dram_timings/Makefile | 2 + .../mach-sunxi/dram_timings/h6_ddr3_1333.c | 144 ++++++++++ .../mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 +++++++++ board/sunxi/MAINTAINERS | 6 + configs/eachlink_h6_mini_defconfig | 17 ++ configs/orangepi_one_plus_defconfig | 1 + configs/pine_h64_defconfig | 1 + 13 files changed, 547 insertions(+), 178 deletions(-) create mode 100644 arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c create mode 100644 configs/eachlink_h6_mini_defconfig

Using memcpy() is, however tempting, not a good idea: It depends on the specific implementation of memcpy, also lacks barriers. In this particular case the first registers were written using 64-bit writes, and the last register using four separate single-byte writes.
Replace the memcpy with a proper loop using the writel() accessor.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/dram_sun50i_h6.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 5da90a2835..e2f141eb9b 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -182,6 +182,7 @@ static void mctl_set_timing_lpddr3(struct dram_para *para) (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; struct sunxi_mctl_phy_reg * const mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i;
u8 tccd = 2; u8 tfaw = max(ns_to_t(50), 4); @@ -237,8 +238,9 @@ static void mctl_set_timing_lpddr3(struct dram_para *para) u8 twr2rd = tcwl + 4 + 1 + twtr; u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1;
- /* set mode register */ - memcpy(mctl_phy->mr, mr_lpddr3, sizeof(mr_lpddr3)); + /* set mode registers */ + for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++) + writel(mr_lpddr3[i], &mctl_phy->mr[i]);
/* set DRAM timing */ writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,

The DRAM controller manual suggests to first program the PHY initialisation parameters to the PHY_PIR register, and then set bit 0 to trigger the initialisation. This is also used in boot0.
Follow this recommendation by setting bit 0 in a separate step.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/dram_sun50i_h6.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index e2f141eb9b..7a8b724f08 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -75,12 +75,14 @@ static void mctl_core_init(struct dram_para *para) mctl_channel_init(para); }
+/* PHY initialisation */ static void mctl_phy_pir_init(u32 val) { struct sunxi_mctl_phy_reg * const mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
- writel(val | BIT(0), &mctl_phy->pir); + writel(val, &mctl_phy->pir); + writel(val | BIT(0), &mctl_phy->pir); /* Start initialisation. */ mctl_await_completion(&mctl_phy->pgsr[0], BIT(0), BIT(0)); }

Currently the H6 DRAM driver only supports one kind of LPDDR3 DRAM. Split the timing parameters for LPDDR3-1333 into a separate file, to allow selecting an alternative later at compile time (as the sunxi-dw driver does).
Signed-off-by: Andre Przywara andre.przywara@arm.com --- .../include/asm/arch-sunxi/dram_sun50i_h6.h | 28 ++++ arch/arm/mach-sunxi/Kconfig | 10 +- arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/dram_sun50i_h6.c | 150 +----------------- arch/arm/mach-sunxi/dram_timings/Makefile | 1 + .../mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 +++++++++++++++ configs/orangepi_one_plus_defconfig | 1 + configs/pine_h64_defconfig | 1 + 8 files changed, 176 insertions(+), 148 deletions(-) create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h index eeb4da5c3f..b28ae583c9 100644 --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h @@ -287,6 +287,32 @@ check_member(sunxi_mctl_phy_reg, dx[3].reserved_0xf0, 0xaf0); #define DCR_DDR4 (4 << 0) #define DCR_DDR8BANK BIT(3)
+ +/* + * The delay parameters below allow to allegedly specify delay times of some + * unknown unit for each individual bit trace in each of the four data bytes + * the 32-bit wide access consists of. Also three control signals can be + * adjusted individually. + */ +#define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE) +/* The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable and DQSN */ +#define WR_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 4) +/* + * The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable, DQSN, + * Termination and Power down + */ +#define RD_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 6) +struct dram_para { + u32 clk; + enum sunxi_dram_type type; + u8 cols; + u8 rows; + u8 ranks; + const u8 dx_read_delays[NR_OF_BYTE_LANES][RD_LINES_PER_BYTE_LANE]; + const u8 dx_write_delays[NR_OF_BYTE_LANES][WR_LINES_PER_BYTE_LANE]; +}; + + static inline int ns_to_t(int nanoseconds) { const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2; @@ -294,4 +320,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); + #endif /* _SUNXI_DRAM_SUN50I_H6_H */ diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 1669e62a6d..e01cb6a09a 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -340,7 +340,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 +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 config SUNXI_DRAM_DDR3 bool
@@ -370,6 +370,14 @@ config SUNXI_DRAM_LPDDR3_STOCK This option is the LPDDR3 timing used by the stock boot0 by Allwinner.
+config SUNXI_DRAM_H6_LPDDR3 + bool "LPDDR3-1333 on the H6 DRAM controller" + select SUNXI_DRAM_LPDDR3 + depends on DRAM_SUN50I_H6 + ---help--- + This option is the LPDDR3 timing used by the stock boot0 by + Allwinner. + config SUNXI_DRAM_DDR2_V3S bool "DDR2 found in V3s chip" select SUNXI_DRAM_DDR2 diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile index 43a93e3085..d129f33479 100644 --- a/arch/arm/mach-sunxi/Makefile +++ b/arch/arm/mach-sunxi/Makefile @@ -39,4 +39,5 @@ obj-$(CONFIG_SPL_SPI_SUNXI) += spl_spi_sunxi.o obj-$(CONFIG_SUNXI_DRAM_DW) += dram_sunxi_dw.o obj-$(CONFIG_SUNXI_DRAM_DW) += dram_timings/ obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o +obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/ endif diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 7a8b724f08..697b8af4ce 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -32,33 +32,8 @@ * similar PHY is ZynqMP. */
-/* - * The delay parameters below allow to allegedly specify delay times of some - * unknown unit for each individual bit trace in each of the four data bytes - * the 32-bit wide access consists of. Also three control signals can be - * adjusted individually. - */ -#define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE) -/* The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable and DQSN */ -#define WR_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 4) -/* - * The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable, DQSN, - * Termination and Power down - */ -#define RD_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 6) -struct dram_para { - u32 clk; - enum sunxi_dram_type type; - u8 cols; - u8 rows; - u8 ranks; - const u8 dx_read_delays[NR_OF_BYTE_LANES][RD_LINES_PER_BYTE_LANE]; - const u8 dx_write_delays[NR_OF_BYTE_LANES][WR_LINES_PER_BYTE_LANE]; -}; - static void mctl_sys_init(struct dram_para *para); static void mctl_com_init(struct dram_para *para); -static void mctl_set_timing_lpddr3(struct dram_para *para); static void mctl_channel_init(struct dram_para *para);
static void mctl_core_init(struct dram_para *para) @@ -67,7 +42,7 @@ static void mctl_core_init(struct dram_para *para) mctl_com_init(para); switch (para->type) { case SUNXI_DRAM_TYPE_LPDDR3: - mctl_set_timing_lpddr3(para); + mctl_set_timing_params(para); break; default: panic("Unsupported DRAM type!"); @@ -171,127 +146,6 @@ static void mctl_set_master_priority(void) MBUS_CONF(HDCP2, true, HIGH, 2, 100, 64, 32); }
-static u32 mr_lpddr3[12] = { - 0x00000000, 0x00000043, 0x0000001a, 0x00000001, - 0x00000000, 0x00000000, 0x00000048, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000003, -}; - -/* TODO: flexible timing */ -static void mctl_set_timing_lpddr3(struct dram_para *para) -{ - struct sunxi_mctl_ctl_reg * const mctl_ctl = - (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; - struct sunxi_mctl_phy_reg * const mctl_phy = - (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; - int i; - - u8 tccd = 2; - u8 tfaw = max(ns_to_t(50), 4); - u8 trrd = max(ns_to_t(10), 2); - u8 trcd = max(ns_to_t(24), 2); - u8 trc = ns_to_t(70); - u8 txp = max(ns_to_t(8), 2); - u8 twtr = max(ns_to_t(8), 2); - u8 trtp = max(ns_to_t(8), 2); - u8 twr = max(ns_to_t(15), 2); - u8 trp = ns_to_t(18); - u8 tras = ns_to_t(42); - u8 twtr_sa = ns_to_t(5); - u8 tcksrea = ns_to_t(11); - u16 trefi = ns_to_t(3900) / 32; - u16 trfc = ns_to_t(210); - u16 txsr = ns_to_t(220); - - if (CONFIG_DRAM_CLK % 400 == 0) { - /* Round up these parameters */ - twtr_sa++; - tcksrea++; - } - - u8 tmrw = 5; - u8 tmrd = 5; - u8 tmod = 12; - u8 tcke = 3; - u8 tcksrx = 5; - u8 tcksre = 5; - u8 tckesr = 5; - u8 trasmax = CONFIG_DRAM_CLK / 60; - u8 txs = 4; - u8 txsdll = 4; - u8 txsabort = 4; - u8 txsfast = 4; - - u8 tcl = 5; /* CL 10 */ - u8 tcwl = 3; /* CWL 6 */ - u8 t_rdata_en = twtr_sa + 8; - - u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ - u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */ - u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */ - u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ - - u8 twtp = tcwl + 4 + twr + 1; - /* - * The code below for twr2rd and trd2wr follows the IP core's - * document from ZynqMP and i.MX7. The BSP has both number - * substracted by 2. - */ - u8 twr2rd = tcwl + 4 + 1 + twtr; - u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1; - - /* set mode registers */ - for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++) - writel(mr_lpddr3[i], &mctl_phy->mr[i]); - - /* 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(txsr, &mctl_ctl->dramtmg[14]); - - clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30)); - writel(0, &mctl_ctl->dfimisc); - clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); - - /* - * Set timing registers of the PHY. - * Note: the PHY is clocked 2x from the DRAM frequency. - */ - writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1), - &mctl_phy->dtpr[0]); - writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]); - writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]); - writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8), - &mctl_phy->dtpr[3]); - writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]); - writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]); - writel(0x0505, &mctl_phy->dtpr[6]); - - /* Configure DFI timing */ - writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000, - &mctl_ctl->dfitmg0); - writel(0x040201, &mctl_ctl->dfitmg1); - - /* Configure PHY timing */ - writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]); - writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]); - - /* set refresh timing */ - writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); -} - static void mctl_sys_init(struct dram_para *para) { struct sunxi_ccm_reg * const ccm = @@ -735,12 +589,14 @@ unsigned long sunxi_dram_init(void) (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; struct dram_para para = { .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 .type = SUNXI_DRAM_TYPE_LPDDR3, .ranks = 2, .cols = 11, .rows = 14, .dx_read_delays = SUN50I_H6_DX_READ_DELAYS, .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS, +#endif };
unsigned long size; diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 278a8a14cc..c3e74362eb 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SUNXI_DRAM_DDR3_1333) += ddr3_1333.o 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_1333.o diff --git a/arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c b/arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c new file mode 100644 index 0000000000..1000860113 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c @@ -0,0 +1,132 @@ +/* + * sun50i H6 LPDDR3 timings + * + * (C) Copyright 2017 Icenowy Zheng icenowy@aosc.io + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +static u32 mr_lpddr3[12] = { + 0x00000000, 0x00000043, 0x0000001a, 0x00000001, + 0x00000000, 0x00000000, 0x00000048, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000003, +}; + +/* TODO: flexible timing */ +void mctl_set_timing_params(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i; + + u8 tccd = 2; + u8 tfaw = max(ns_to_t(50), 4); + u8 trrd = max(ns_to_t(10), 2); + u8 trcd = max(ns_to_t(24), 2); + u8 trc = ns_to_t(70); + u8 txp = max(ns_to_t(8), 2); + u8 twtr = max(ns_to_t(8), 2); + u8 trtp = max(ns_to_t(8), 2); + u8 twr = max(ns_to_t(15), 2); + u8 trp = ns_to_t(18); + u8 tras = ns_to_t(42); + u8 twtr_sa = ns_to_t(5); + u8 tcksrea = ns_to_t(11); + u16 trefi = ns_to_t(3900) / 32; + u16 trfc = ns_to_t(210); + u16 txsr = ns_to_t(220); + + if (CONFIG_DRAM_CLK % 400 == 0) { + /* Round up these parameters */ + twtr_sa++; + tcksrea++; + } + + u8 tmrw = 5; + u8 tmrd = 5; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 5; + u8 trasmax = CONFIG_DRAM_CLK / 60; + u8 txs = 4; + u8 txsdll = 4; + u8 txsabort = 4; + u8 txsfast = 4; + + u8 tcl = 5; /* CL 10 */ + u8 tcwl = 3; /* CWL 6 */ + u8 t_rdata_en = twtr_sa + 8; + + u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */ + u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 4 + twr + 1; + /* + * The code below for twr2rd and trd2wr follows the IP core's + * document from ZynqMP and i.MX7. The BSP has both number + * substracted by 2. + */ + u8 twr2rd = tcwl + 4 + 1 + twtr; + u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1; + + /* set mode registers */ + for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++) + writel(mr_lpddr3[i], &mctl_phy->mr[i]); + + /* 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(txsr, &mctl_ctl->dramtmg[14]); + + clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30)); + writel(0, &mctl_ctl->dfimisc); + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + + /* + * Set timing registers of the PHY. + * Note: the PHY is clocked 2x from the DRAM frequency. + */ + writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1), + &mctl_phy->dtpr[0]); + writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]); + writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]); + writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8), + &mctl_phy->dtpr[3]); + writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]); + writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]); + writel(0x0505, &mctl_phy->dtpr[6]); + + /* Configure DFI timing */ + writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000, + &mctl_ctl->dfitmg0); + writel(0x040201, &mctl_ctl->dfitmg1); + + /* Configure PHY timing */ + writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]); + writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]); + + /* set refresh timing */ + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); +} diff --git a/configs/orangepi_one_plus_defconfig b/configs/orangepi_one_plus_defconfig index 65537c422f..e4b9f3a1c8 100644 --- a/configs/orangepi_one_plus_defconfig +++ b/configs/orangepi_one_plus_defconfig @@ -3,6 +3,7 @@ CONFIG_ARCH_SUNXI=y CONFIG_NR_DRAM_BANKS=1 CONFIG_SPL=y CONFIG_MACH_SUN50I_H6=y +CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MMC0_CD_PIN="PF6" # CONFIG_PSCI_RESET is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 5ac89b462c..d88a6b1fd8 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -3,6 +3,7 @@ CONFIG_ARCH_SUNXI=y CONFIG_NR_DRAM_BANKS=1 CONFIG_SPL=y CONFIG_MACH_SUN50I_H6=y +CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MMC0_CD_PIN="PF6" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 # CONFIG_PSCI_RESET is not set

于 2019年7月2日 GMT+08:00 下午5:51:44, Andre Przywara andre.przywara@arm.com 写到:
Currently the H6 DRAM driver only supports one kind of LPDDR3 DRAM. Split the timing parameters for LPDDR3-1333 into a separate file, to
I don't think it's 1333.
H6 memory controller says 800MHz maximum for LPDDR3 and 933 for DDR3/DDR4.
And currently we run at 744MHz on Pine H64.
allow selecting an alternative later at compile time (as the sunxi-dw driver does).
Signed-off-by: Andre Przywara andre.przywara@arm.com
.../include/asm/arch-sunxi/dram_sun50i_h6.h | 28 ++++ arch/arm/mach-sunxi/Kconfig | 10 +- arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/dram_sun50i_h6.c | 150 +----------------- arch/arm/mach-sunxi/dram_timings/Makefile | 1 + .../mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 +++++++++++++++ configs/orangepi_one_plus_defconfig | 1 + configs/pine_h64_defconfig | 1 + 8 files changed, 176 insertions(+), 148 deletions(-) create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h index eeb4da5c3f..b28ae583c9 100644 --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h @@ -287,6 +287,32 @@ check_member(sunxi_mctl_phy_reg, dx[3].reserved_0xf0, 0xaf0); #define DCR_DDR4 (4 << 0) #define DCR_DDR8BANK BIT(3)
+/*
- The delay parameters below allow to allegedly specify delay times
of some
- unknown unit for each individual bit trace in each of the four data
bytes
- the 32-bit wide access consists of. Also three control signals can
be
- adjusted individually.
- */
+#define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE) +/* The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable and DQSN */ +#define WR_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 4) +/*
- The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable,
DQSN,
- Termination and Power down
- */
+#define RD_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 6) +struct dram_para {
- u32 clk;
- enum sunxi_dram_type type;
- u8 cols;
- u8 rows;
- u8 ranks;
- const u8 dx_read_delays[NR_OF_BYTE_LANES][RD_LINES_PER_BYTE_LANE];
- const u8 dx_write_delays[NR_OF_BYTE_LANES][WR_LINES_PER_BYTE_LANE];
+};
static inline int ns_to_t(int nanoseconds) { const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2; @@ -294,4 +320,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);
#endif /* _SUNXI_DRAM_SUN50I_H6_H */ diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 1669e62a6d..e01cb6a09a 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -340,7 +340,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 +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 config SUNXI_DRAM_DDR3 bool
@@ -370,6 +370,14 @@ config SUNXI_DRAM_LPDDR3_STOCK This option is the LPDDR3 timing used by the stock boot0 by Allwinner.
+config SUNXI_DRAM_H6_LPDDR3
- bool "LPDDR3-1333 on the H6 DRAM controller"
- select SUNXI_DRAM_LPDDR3
- depends on DRAM_SUN50I_H6
- ---help---
- This option is the LPDDR3 timing used by the stock boot0 by
- Allwinner.
config SUNXI_DRAM_DDR2_V3S bool "DDR2 found in V3s chip" select SUNXI_DRAM_DDR2 diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile index 43a93e3085..d129f33479 100644 --- a/arch/arm/mach-sunxi/Makefile +++ b/arch/arm/mach-sunxi/Makefile @@ -39,4 +39,5 @@ obj-$(CONFIG_SPL_SPI_SUNXI) += spl_spi_sunxi.o obj-$(CONFIG_SUNXI_DRAM_DW) += dram_sunxi_dw.o obj-$(CONFIG_SUNXI_DRAM_DW) += dram_timings/ obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o +obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/ endif diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 7a8b724f08..697b8af4ce 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -32,33 +32,8 @@
- similar PHY is ZynqMP.
*/
-/*
- The delay parameters below allow to allegedly specify delay times
of some
- unknown unit for each individual bit trace in each of the four data
bytes
- the 32-bit wide access consists of. Also three control signals can
be
- adjusted individually.
- */
-#define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE) -/* The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable and DQSN */ -#define WR_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 4) -/*
- The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable,
DQSN,
- Termination and Power down
- */
-#define RD_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 6) -struct dram_para {
- u32 clk;
- enum sunxi_dram_type type;
- u8 cols;
- u8 rows;
- u8 ranks;
- const u8 dx_read_delays[NR_OF_BYTE_LANES][RD_LINES_PER_BYTE_LANE];
- const u8 dx_write_delays[NR_OF_BYTE_LANES][WR_LINES_PER_BYTE_LANE];
-};
static void mctl_sys_init(struct dram_para *para); static void mctl_com_init(struct dram_para *para); -static void mctl_set_timing_lpddr3(struct dram_para *para); static void mctl_channel_init(struct dram_para *para);
static void mctl_core_init(struct dram_para *para) @@ -67,7 +42,7 @@ static void mctl_core_init(struct dram_para *para) mctl_com_init(para); switch (para->type) { case SUNXI_DRAM_TYPE_LPDDR3:
mctl_set_timing_lpddr3(para);
break; default: panic("Unsupported DRAM type!");mctl_set_timing_params(para);
@@ -171,127 +146,6 @@ static void mctl_set_master_priority(void) MBUS_CONF(HDCP2, true, HIGH, 2, 100, 64, 32); }
-static u32 mr_lpddr3[12] = {
- 0x00000000, 0x00000043, 0x0000001a, 0x00000001,
- 0x00000000, 0x00000000, 0x00000048, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000003,
-};
-/* TODO: flexible timing */ -static void mctl_set_timing_lpddr3(struct dram_para *para) -{
- struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
- struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
- int i;
- u8 tccd = 2;
- u8 tfaw = max(ns_to_t(50), 4);
- u8 trrd = max(ns_to_t(10), 2);
- u8 trcd = max(ns_to_t(24), 2);
- u8 trc = ns_to_t(70);
- u8 txp = max(ns_to_t(8), 2);
- u8 twtr = max(ns_to_t(8), 2);
- u8 trtp = max(ns_to_t(8), 2);
- u8 twr = max(ns_to_t(15), 2);
- u8 trp = ns_to_t(18);
- u8 tras = ns_to_t(42);
- u8 twtr_sa = ns_to_t(5);
- u8 tcksrea = ns_to_t(11);
- u16 trefi = ns_to_t(3900) / 32;
- u16 trfc = ns_to_t(210);
- u16 txsr = ns_to_t(220);
- if (CONFIG_DRAM_CLK % 400 == 0) {
/* Round up these parameters */
twtr_sa++;
tcksrea++;
- }
- u8 tmrw = 5;
- u8 tmrd = 5;
- u8 tmod = 12;
- u8 tcke = 3;
- u8 tcksrx = 5;
- u8 tcksre = 5;
- u8 tckesr = 5;
- u8 trasmax = CONFIG_DRAM_CLK / 60;
- u8 txs = 4;
- u8 txsdll = 4;
- u8 txsabort = 4;
- u8 txsfast = 4;
- u8 tcl = 5; /* CL 10 */
- u8 tcwl = 3; /* CWL 6 */
- u8 t_rdata_en = twtr_sa + 8;
- u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
- u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */
- u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */
- u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
- u8 twtp = tcwl + 4 + twr + 1;
- /*
* The code below for twr2rd and trd2wr follows the IP core's
* document from ZynqMP and i.MX7. The BSP has both number
* substracted by 2.
*/
- u8 twr2rd = tcwl + 4 + 1 + twtr;
- u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1;
- /* set mode registers */
- for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++)
writel(mr_lpddr3[i], &mctl_phy->mr[i]);
- /* 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(txsr, &mctl_ctl->dramtmg[14]);
- clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30));
- writel(0, &mctl_ctl->dfimisc);
- clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
- /*
* Set timing registers of the PHY.
* Note: the PHY is clocked 2x from the DRAM frequency.
*/
- writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1),
&mctl_phy->dtpr[0]);
- writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]);
- writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]);
- writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8),
&mctl_phy->dtpr[3]);
- writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]);
- writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]);
- writel(0x0505, &mctl_phy->dtpr[6]);
- /* Configure DFI timing */
- writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
- writel(0x040201, &mctl_ctl->dfitmg1);
- /* Configure PHY timing */
- writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]);
- writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]);
- /* set refresh timing */
- writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
-}
static void mctl_sys_init(struct dram_para *para) { struct sunxi_ccm_reg * const ccm = @@ -735,12 +589,14 @@ unsigned long sunxi_dram_init(void) (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; struct dram_para para = { .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 .type = SUNXI_DRAM_TYPE_LPDDR3, .ranks = 2, .cols = 11, .rows = 14, .dx_read_delays = SUN50I_H6_DX_READ_DELAYS, .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS, +#endif };
unsigned long size; diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 278a8a14cc..c3e74362eb 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SUNXI_DRAM_DDR3_1333) += ddr3_1333.o 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_1333.o diff --git a/arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c b/arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c new file mode 100644 index 0000000000..1000860113 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c @@ -0,0 +1,132 @@ +/*
- sun50i H6 LPDDR3 timings
- (C) Copyright 2017 Icenowy Zheng icenowy@aosc.io
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h>
+static u32 mr_lpddr3[12] = {
- 0x00000000, 0x00000043, 0x0000001a, 0x00000001,
- 0x00000000, 0x00000000, 0x00000048, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000003,
+};
+/* TODO: flexible timing */ +void mctl_set_timing_params(struct dram_para *para) +{
- struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
- struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
- int i;
- u8 tccd = 2;
- u8 tfaw = max(ns_to_t(50), 4);
- u8 trrd = max(ns_to_t(10), 2);
- u8 trcd = max(ns_to_t(24), 2);
- u8 trc = ns_to_t(70);
- u8 txp = max(ns_to_t(8), 2);
- u8 twtr = max(ns_to_t(8), 2);
- u8 trtp = max(ns_to_t(8), 2);
- u8 twr = max(ns_to_t(15), 2);
- u8 trp = ns_to_t(18);
- u8 tras = ns_to_t(42);
- u8 twtr_sa = ns_to_t(5);
- u8 tcksrea = ns_to_t(11);
- u16 trefi = ns_to_t(3900) / 32;
- u16 trfc = ns_to_t(210);
- u16 txsr = ns_to_t(220);
- if (CONFIG_DRAM_CLK % 400 == 0) {
/* Round up these parameters */
twtr_sa++;
tcksrea++;
- }
- u8 tmrw = 5;
- u8 tmrd = 5;
- u8 tmod = 12;
- u8 tcke = 3;
- u8 tcksrx = 5;
- u8 tcksre = 5;
- u8 tckesr = 5;
- u8 trasmax = CONFIG_DRAM_CLK / 60;
- u8 txs = 4;
- u8 txsdll = 4;
- u8 txsabort = 4;
- u8 txsfast = 4;
- u8 tcl = 5; /* CL 10 */
- u8 tcwl = 3; /* CWL 6 */
- u8 t_rdata_en = twtr_sa + 8;
- u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
- u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */
- u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */
- u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
- u8 twtp = tcwl + 4 + twr + 1;
- /*
* The code below for twr2rd and trd2wr follows the IP core's
* document from ZynqMP and i.MX7. The BSP has both number
* substracted by 2.
*/
- u8 twr2rd = tcwl + 4 + 1 + twtr;
- u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1;
- /* set mode registers */
- for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++)
writel(mr_lpddr3[i], &mctl_phy->mr[i]);
- /* 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(txsr, &mctl_ctl->dramtmg[14]);
- clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30));
- writel(0, &mctl_ctl->dfimisc);
- clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
- /*
* Set timing registers of the PHY.
* Note: the PHY is clocked 2x from the DRAM frequency.
*/
- writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1),
&mctl_phy->dtpr[0]);
- writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]);
- writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]);
- writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8),
&mctl_phy->dtpr[3]);
- writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]);
- writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]);
- writel(0x0505, &mctl_phy->dtpr[6]);
- /* Configure DFI timing */
- writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
- writel(0x040201, &mctl_ctl->dfitmg1);
- /* Configure PHY timing */
- writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]);
- writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]);
- /* set refresh timing */
- writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+} diff --git a/configs/orangepi_one_plus_defconfig b/configs/orangepi_one_plus_defconfig index 65537c422f..e4b9f3a1c8 100644 --- a/configs/orangepi_one_plus_defconfig +++ b/configs/orangepi_one_plus_defconfig @@ -3,6 +3,7 @@ CONFIG_ARCH_SUNXI=y CONFIG_NR_DRAM_BANKS=1 CONFIG_SPL=y CONFIG_MACH_SUN50I_H6=y +CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MMC0_CD_PIN="PF6" # CONFIG_PSCI_RESET is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 5ac89b462c..d88a6b1fd8 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -3,6 +3,7 @@ CONFIG_ARCH_SUNXI=y CONFIG_NR_DRAM_BANKS=1 CONFIG_SPL=y CONFIG_MACH_SUN50I_H6=y +CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MMC0_CD_PIN="PF6" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 # CONFIG_PSCI_RESET is not set

At the moment the H6 DRAM driver only supports LPDDR3 DRAM.
Extend the driver to cover DDR3 DRAM as well.
The changes are partly motivated by looking at the ZynqMP register documentation, partly by looking at register dumps after boot0/libdram has initialised the controller.
Many thanks to Jernej for contributing some fixes!
Signed-off-by: Andre Przywara andre.przywara@arm.com --- .../include/asm/arch-sunxi/dram_sun50i_h6.h | 9 +- arch/arm/mach-sunxi/dram_sun50i_h6.c | 82 ++++++++++++------- 2 files changed, 62 insertions(+), 29 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h index b28ae583c9..f0ae72eb2c 100644 --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h @@ -9,6 +9,8 @@ #ifndef _SUNXI_DRAM_SUN50I_H6_H #define _SUNXI_DRAM_SUN50I_H6_H
+#include <stdbool.h> + enum sunxi_dram_type { SUNXI_DRAM_TYPE_DDR3 = 3, SUNXI_DRAM_TYPE_DDR4, @@ -16,6 +18,11 @@ enum sunxi_dram_type { SUNXI_DRAM_TYPE_LPDDR3, };
+static inline bool sunxi_dram_is_lpddr(int type) +{ + return type >= SUNXI_DRAM_TYPE_LPDDR2; +} + /* * The following information is mainly retrieved by disassembly and some FPGA * test code of sun50iw3 platform. @@ -286,7 +293,7 @@ check_member(sunxi_mctl_phy_reg, dx[3].reserved_0xf0, 0xaf0); #define DCR_DDR3 (3 << 0) #define DCR_DDR4 (4 << 0) #define DCR_DDR8BANK BIT(3) - +#define DCR_DDR2T BIT(28)
/* * The delay parameters below allow to allegedly specify delay times of some diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 697b8af4ce..17649ffbf9 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -42,6 +42,7 @@ static void mctl_core_init(struct dram_para *para) mctl_com_init(para); switch (para->type) { case SUNXI_DRAM_TYPE_LPDDR3: + case SUNXI_DRAM_TYPE_DDR3: mctl_set_timing_params(para); break; default: @@ -284,14 +285,11 @@ static void mctl_com_init(struct dram_para *para) mctl_set_addrmap(para);
setbits_le32(&mctl_com->cr, BIT(31)); - /* - * This address is magic; it's in SID memory area, but there's no - * known definition of it. - * On my Pine H64 board it has content 7. - */ - if (readl(0x03006100) == 7) + + /* The bonding ID seems to be always 7. */ + if (readl(SUNXI_SIDC_BASE + 0x100) == 7) /* bonding ID */ clrbits_le32(&mctl_com->cr, BIT(27)); - else if (readl(0x03006100) == 3) + else if (readl(SUNXI_SIDC_BASE + 0x100) == 3) setbits_le32(&mctl_com->cr, BIT(27));
if (para->clk > 408) @@ -302,22 +300,37 @@ static void mctl_com_init(struct dram_para *para) reg_val = 0x3f00; clrsetbits_le32(&mctl_com->unk_0x008, 0x3f00, reg_val);
- /* TODO: half DQ, non-LPDDR3 types */ - writel(MSTR_DEVICETYPE_LPDDR3 | MSTR_BUSWIDTH_FULL | - MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks) | - 0x80000000, &mctl_ctl->mstr); - writel(DCR_LPDDR3 | DCR_DDR8BANK | 0x400, &mctl_phy->dcr); + /* TODO: half DQ, DDR4 */ + reg_val = MSTR_BUSWIDTH_FULL | MSTR_BURST_LENGTH(8) | + MSTR_ACTIVE_RANKS(para->ranks); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + reg_val |= MSTR_DEVICETYPE_LPDDR3; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; + writel(reg_val | BIT(31), &mctl_ctl->mstr); + + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + reg_val = DCR_LPDDR3 | DCR_DDR8BANK; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + reg_val = DCR_DDR3 | DCR_DDR8BANK | DCR_DDR2T; + writel(reg_val | 0x400, &mctl_phy->dcr);
if (para->ranks == 2) writel(0x0303, &mctl_ctl->odtmap); else writel(0x0201, &mctl_ctl->odtmap);
- /* TODO: non-LPDDR3 types */ - tmp = para->clk * 7 / 2000; - reg_val = 0x0400; - reg_val |= (tmp + 7) << 24; - reg_val |= (((para->clk < 400) ? 3 : 4) - tmp) << 16; + /* TODO: DDR4 */ + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + tmp = para->clk * 7 / 2000; + reg_val = 0x0400; + reg_val |= (tmp + 7) << 24; + reg_val |= (((para->clk < 400) ? 3 : 4) - tmp) << 16; + } else if (para->type == SUNXI_DRAM_TYPE_DDR3) { + reg_val = 0x06000400; /* TODO?: Use CL - CWL value in [7:0] */ + } else { + panic("Only (LP)DDR3 supported (type = %d)\n", para->type); + } writel(reg_val, &mctl_ctl->odtcfg);
/* TODO: half DQ */ @@ -372,6 +385,9 @@ static void mctl_bit_delay_set(struct dram_para *para) setbits_le32(&mctl_phy->pgcr[0], BIT(26)); udelay(1);
+ if (para->type != SUNXI_DRAM_TYPE_LPDDR3) + return; + for (i = 1; i < 14; i++) { val = readl(&mctl_phy->acbdlr[i]); val += 0x0a0a0a0a; @@ -419,7 +435,8 @@ static void mctl_channel_init(struct dram_para *para) else clrsetbits_le32(&mctl_phy->dtcr[1], 0x30000, 0x10000);
- clrbits_le32(&mctl_phy->dtcr[1], BIT(1)); + if (sunxi_dram_is_lpddr(para->type)) + clrbits_le32(&mctl_phy->dtcr[1], BIT(1)); if (para->ranks == 2) { writel(0x00010001, &mctl_phy->rankidr); writel(0x20000, &mctl_phy->odtcr); @@ -428,8 +445,11 @@ static void mctl_channel_init(struct dram_para *para) writel(0x10000, &mctl_phy->odtcr); }
- /* TODO: non-LPDDR3 types */ - clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000040); + /* set bits [3:0] to 1? 0 not valid in ZynqMP d/s */ + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000040); + else + clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000000); if (para->clk <= 792) { if (para->clk <= 672) { if (para->clk <= 600) @@ -459,12 +479,13 @@ static void mctl_channel_init(struct dram_para *para) writel(0x06060606, &mctl_phy->acbdlr[i]); }
- /* TODO: non-LPDDR3 types */ - mctl_phy_pir_init(PIR_ZCAL | PIR_DCAL | PIR_PHYRST | PIR_DRAMINIT | - PIR_QSGATE | PIR_RDDSKW | PIR_WRDSKW | PIR_RDEYE | - PIR_WREYE); + val = PIR_ZCAL | PIR_DCAL | PIR_PHYRST | PIR_DRAMINIT | PIR_QSGATE | + PIR_RDDSKW | PIR_WRDSKW | PIR_RDEYE | PIR_WREYE; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + val |= PIR_DRAMRST | PIR_WL; + mctl_phy_pir_init(val);
- /* TODO: non-LPDDR3 types */ + /* TODO: DDR4 types ? */ for (i = 0; i < 4; i++) writel(0x00000909, &mctl_phy->dx[i].gcr[5]);
@@ -520,7 +541,8 @@ static void mctl_channel_init(struct dram_para *para) panic("Error while initializing DRAM PHY!\n"); }
- clrsetbits_le32(&mctl_phy->dsgcr, 0xc0, 0x40); + if (sunxi_dram_is_lpddr(para->type)) + clrsetbits_le32(&mctl_phy->dsgcr, 0xc0, 0x40); clrbits_le32(&mctl_phy->pgcr[1], 0x40); clrbits_le32(&mctl_ctl->dfimisc, BIT(0)); writel(1, &mctl_ctl->swctl); @@ -589,11 +611,15 @@ unsigned long sunxi_dram_init(void) (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; struct dram_para para = { .clk = CONFIG_DRAM_CLK, -#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 - .type = SUNXI_DRAM_TYPE_LPDDR3, .ranks = 2, .cols = 11, .rows = 14, +#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 + .type = SUNXI_DRAM_TYPE_LPDDR3, + .dx_read_delays = SUN50I_H6_DX_READ_DELAYS, + .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS, +#elif defined(CONFIG_SUNXI_DRAM_H6_DDR3_1333) + .type = SUNXI_DRAM_TYPE_DDR3, .dx_read_delays = SUN50I_H6_DX_READ_DELAYS, .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS, #endif

Add a routine to program the timing parameters for DDR3-1333 DRAM chips connected to the H6 DRAM controller.
The values were gathered from doing back-calculations from a register dump, trying to match them up with the official JEDEC DDDR3 spec. If in doubt, the register dump values were taken for now, but the JEDEC recommendation were added as a comment.
Many thanks to Jernej for contributing fixes!
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/Kconfig | 8 + arch/arm/mach-sunxi/dram_timings/Makefile | 1 + .../mach-sunxi/dram_timings/h6_ddr3_1333.c | 144 ++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index e01cb6a09a..12fa3ad811 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -378,6 +378,14 @@ config SUNXI_DRAM_H6_LPDDR3 This option is the LPDDR3 timing used by the stock boot0 by Allwinner.
+config SUNXI_DRAM_H6_DDR3_1333 + bool "DDR3-1333 boot0 timings on the H6 DRAM controller" + select SUNXI_DRAM_DDR3 + depends on DRAM_SUN50I_H6 + ---help--- + This option is the DDR3 timing used by the boot0 on H6 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 c3e74362eb..45f2e5a6b9 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_SUNXI_DRAM_DDR3_1333) += ddr3_1333.o 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_1333.o +obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o diff --git a/arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c b/arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c new file mode 100644 index 0000000000..611eaa3024 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c @@ -0,0 +1,144 @@ +/* + * sun50i H6 DDR3-1333 timings, as programmed by Allwinner's boot0 + * for some TV boxes with the H6 and DDR3 memory. + * + * The chips are probably able to be driven by a faster clock, but boot0 + * uses a more conservative timing (as usual). + * + * (C) Copyright 2018,2019 Arm Ltd. + * based on previous work by: + * (C) Copyright 2017 Icenowy Zheng icenowy@aosc.io + * + * References used: + * - JEDEC DDR3 SDRAM standard: JESD79-3F.pdf + * - Samsung K4B2G0446D datasheet + * - ZynqMP UG1087 register DDRC/PHY documentation + * + * Many thanks to Jernej Skrabec for contributing some fixes! + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +/* + * Only the first four are used for DDR3(?) + * MR0: BL8, seq. read burst, no test, fast exit (DLL on), no DLL reset, + * CAS latency (CL): 11, write recovery (WR): 12 + * MR1: DLL enabled, output strength RZQ/6, Rtt_norm RZQ/2, + * write levelling disabled, TDQS disabled, output buffer enabled + * MR2: manual full array self refresh, dynamic ODT off, + * CAS write latency (CWL): 8 + */ +static u32 mr_ddr3[7] = { + 0x00001c70, 0x00000040, 0x00000018, 0x00000000, + 0x00000000, 0x00000400, 0x00000848, +}; + +/* TODO: flexible timing */ +void mctl_set_timing_params(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i; + + u8 tccd = 2; /* JEDEC: 4nCK */ + u8 tfaw = ns_to_t(50); /* JEDEC: 30 ns w/ 1K pages */ + u8 trrd = max(ns_to_t(6), 4); /* JEDEC: max(6 ns, 4nCK) */ + u8 trcd = ns_to_t(15); /* JEDEC: 13.5 ns */ + u8 trc = ns_to_t(53); /* JEDEC: 49.5 ns */ + u8 txp = max(ns_to_t(6), 3); /* JEDEC: max(6 ns, 3nCK) */ + u8 twtr = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */ + u8 trtp = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */ + u8 twr = ns_to_t(15); /* JEDEC: 15 ns */ + u8 trp = ns_to_t(15); /* JEDEC: >= 13.75 ns */ + u8 tras = ns_to_t(38); /* JEDEC >= 36 ns, <= 9*trefi */ + u8 twtr_sa = 2; /* ? */ + u8 tcksrea = 4; /* ? */ + u16 trefi = ns_to_t(7800) / 32; /* JEDEC: 7.8us@Tcase <= 85C */ + u16 trfc = ns_to_t(350); /* JEDEC: 160 ns for 2Gb */ + u16 txsr = 4; /* ? */ + + u8 tmrw = 0; /* ? */ + u8 tmrd = 4; /* JEDEC: 4nCK */ + u8 tmod = max(ns_to_t(15), 12); /* JEDEC: max(15 ns, 12nCK) */ + u8 tcke = max(ns_to_t(6), 3); /* JEDEC: max(5.625 ns, 3nCK) */ + u8 tcksrx = max(ns_to_t(10), 5); /* JEDEC: max(10 ns, 5nCK) */ + u8 tcksre = max(ns_to_t(10), 5); /* JEDEC: max(10 ns, 5nCK) */ + u8 tckesr = tcke + 1; /* JEDEC: tCKE(min) + 1nCK */ + u8 trasmax = 24; /* JEDEC: tREFI * 9 */ + u8 txs = ns_to_t(360) / 32; /* JEDEC: max(5nCK,tRFC+10ns) */ + u8 txsdll = 4; /* JEDEC: 512 nCK */ + u8 txsabort = 4; /* ? */ + u8 txsfast = 4; /* ? */ + u8 tcl = 6; /* JEDEC: CL / 2 => 6 */ + u8 tcwl = 4; /* JEDEC: 8 */ + u8 t_rdata_en = 7; /* ? */ + + u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */ + u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; + u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 2 + twr; /* (WL + BL / 2 + tWR) / 2 */ + u8 twr2rd = tcwl + 2 + twtr; /* (WL + BL / 2 + tWTR) / 2 */ + u8 trd2wr = 5; /* (RL + BL / 2 + 2 - WL) / 2 */ + + if (tcl + 1 >= trtp + trp) + trtp = tcl + 2 - trp; + + /* set mode registers */ + for (i = 0; i < ARRAY_SIZE(mr_ddr3); i++) + writel(mr_ddr3[i], &mctl_phy->mr[i]); + + /* 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(txsr, &mctl_ctl->dramtmg[14]); + + clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30)); + writel(0, &mctl_ctl->dfimisc); + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + + /* + * Set timing registers of the PHY. + * Note: the PHY is clocked 2x from the DRAM frequency. + */ + writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1), + &mctl_phy->dtpr[0]); + writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]); + writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]); + writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8), + &mctl_phy->dtpr[3]); + writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]); + writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]); + writel(0x0505, &mctl_phy->dtpr[6]); + + /* Configure DFI timing */ + writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000, + &mctl_ctl->dfitmg0); + writel(0x040201, &mctl_ctl->dfitmg1); + + /* Configure PHY timing. Zynq uses different registers. */ + writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]); + writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]); + + /* set refresh timing */ + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); +}

From: Jernej Skrabec jernej.skrabec@siol.net
Add some basic line delay values to be used with DDR3 DRAM chips on some H6 TV boxes. Taken from a register dump after boot0 initialised the DRAM. Put them as the default delay values for DDR3 DRAM until we know better.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/dram_sun50i_h6.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 17649ffbf9..2a8275da3a 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -594,17 +594,28 @@ unsigned long mctl_calc_size(struct dram_para *para) return (1ULL << (para->cols + para->rows + 3)) * 4 * para->ranks; }
-#define SUN50I_H6_DX_WRITE_DELAYS \ +#define SUN50I_H6_LPDDR3_DX_WRITE_DELAYS \ {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0 }, \ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} -#define SUN50I_H6_DX_READ_DELAYS \ +#define SUN50I_H6_LPDDR3_DX_READ_DELAYS \ {{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }}
+#define SUN50I_H6_DDR3_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN50I_H6_DDR3_DX_READ_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} + unsigned long sunxi_dram_init(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -616,12 +627,12 @@ unsigned long sunxi_dram_init(void) .rows = 14, #ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 .type = SUNXI_DRAM_TYPE_LPDDR3, - .dx_read_delays = SUN50I_H6_DX_READ_DELAYS, - .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS, + .dx_read_delays = SUN50I_H6_LPDDR3_DX_READ_DELAYS, + .dx_write_delays = SUN50I_H6_LPDDR3_DX_WRITE_DELAYS, #elif defined(CONFIG_SUNXI_DRAM_H6_DDR3_1333) .type = SUNXI_DRAM_TYPE_DDR3, - .dx_read_delays = SUN50I_H6_DX_READ_DELAYS, - .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS, + .dx_read_delays = SUN50I_H6_DDR3_DX_READ_DELAYS, + .dx_write_delays = SUN50I_H6_DDR3_DX_WRITE_DELAYS, #endif };

The Eachlink H6 Mini is a modestly priced TV box, using the Allwinner H6 SoC. It comes with 3GB of DRAM and 32GB of eMMC in the typical TV box enclosure.
This adds a basic device tree and defconfig for it.
It contrast to the other supported H6 boards the H6 Mini uses DDR3 DRAM chips (not LPDDR3), which require a different DRAM controller setup.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/dts/Makefile | 1 + arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts | 116 ++++++++++++++++++++ board/sunxi/MAINTAINERS | 6 + configs/eachlink_h6_mini_defconfig | 17 +++ 4 files changed, 140 insertions(+) create mode 100644 arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts create mode 100644 configs/eachlink_h6_mini_defconfig
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 7fbd0a1d06..331ea720d4 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -509,6 +509,7 @@ dtb-$(CONFIG_MACH_SUN50I_H5) += \ sun50i-h5-orangepi-zero-plus2.dtb dtb-$(CONFIG_MACH_SUN50I_H6) += \ sun50i-h6-beelink-gs1.dtb \ + sun50i-h6-eachlink-h6-mini.dtb \ sun50i-h6-orangepi-lite2.dtb \ sun50i-h6-orangepi-one-plus.dtb \ sun50i-h6-pine-h64.dtb diff --git a/arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts b/arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts new file mode 100644 index 0000000000..5956b5ccd7 --- /dev/null +++ b/arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (c) 2018 Arm Ltd. + * based on work by: + * Copyright (c) 2017 Icenowy Zheng icenowy@aosc.io + */ + +/dts-v1/; + +#include "sun50i-h6.dtsi" + +#include <dt-bindings/gpio/gpio.h> + +/ { + model = "Eachlink H6 Mini"; + compatible = "eachlink,h6-mini", "allwinner,sun50i-h6"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + reg_vcc3v3: vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_vcc5v: vcc5v { + /* supply from the micro-USB DC jack */ + compatible = "regulator-fixed"; + regulator-name = "vcc-5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; +}; + +&de { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + +&ehci0 { + phys = <&usb2phy 0>; + status = "okay"; +}; + +&ehci3 { + status = "okay"; +}; + +&mmc0 { + vmmc-supply = <®_vcc3v3>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; + bus-width = <4>; + status = "okay"; +}; + +&mmc2 { + vmmc-supply = <®_vcc3v3>; + vqmmc-supply = <®_vcc3v3>; + non-removable; + cap-mmc-hw-reset; + bus-width = <8>; + status = "okay"; +}; + +&ohci0 { + phys = <&usb2phy 0>; + status = "okay"; +}; + +&ohci3 { + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; + status = "okay"; +}; + +&usb2otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb2phy { + usb0_vbus-supply = <®_vcc5v>; + status = "okay"; +}; diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS index bdd1854197..fa8c2ef181 100644 --- a/board/sunxi/MAINTAINERS +++ b/board/sunxi/MAINTAINERS @@ -195,6 +195,12 @@ M: Chen-Yu Tsai wens@csie.org S: Maintained F: configs/Cubietruck_plus_defconfig
+EACHLINK H6 MINI +M: Andre Przywara andre.przywara@arm.com +S: Maintained +F: configs/eachlink_h6_mini_defconfig +F: arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts + EMLID NEUTIS N5 DEV BOARD M: Aleksandr Aleksandrov aleksandr.aleksandrov@emlid.com S: Maintained diff --git a/configs/eachlink_h6_mini_defconfig b/configs/eachlink_h6_mini_defconfig new file mode 100644 index 0000000000..d471a24dd5 --- /dev/null +++ b/configs/eachlink_h6_mini_defconfig @@ -0,0 +1,17 @@ +CONFIG_ARM=y +CONFIG_ARCH_SUNXI=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_SPL=y +CONFIG_MACH_SUN50I_H6=y +CONFIG_DRAM_CLK=660 +CONFIG_DRAM_ZQ=3881979 +CONFIG_SUNXI_DRAM_H6_DDR3_1333=y +CONFIG_MMC0_CD_PIN="PF6" +CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +# CONFIG_PSCI_RESET is not set +# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set +CONFIG_SPL_TEXT_BASE=0x20060 +# CONFIG_CMD_FLASH is not set +# CONFIG_SPL_DOS_PARTITION is not set +# CONFIG_SPL_EFI_PARTITION is not set +CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-eachlink-h6-mini"

Dne torek, 02. julij 2019 ob 11:51:41 CEST je Andre Przywara napisal(a):
An updated version, with minor changes. I realised that my comments about JEDEC values were still based on the DDR3-1600 speed bin, so I updated those to match the DDR3-1333 values. I also used the actual JEDEC recommended formulas where that didn't change the actual value written. For more details see the Changelog below.
So far the SPL DRAM driver for the Allwinner H6 SoC only supports LPDDR3 DRAM chips, which are used on most single board computers with this SoC. There are some TV boxes with the H6 out now, but most of them are using DDR3 DRAM instead of LPDDR3.
This series extends the existing H6 DRAM driver to cover DDR3 DRAMs as well. The information used in these patches is from:
- register dumps after Allwinner's boot0 (libdram) has initialised the DRAM
- some disassembly of the libdram library
- timing parameters as found in the boot0 binary
- comparison with Xilinx ZynqMP DRAM controller documentation
The box I played with (Eachlink H6 Mini) has 3GB of DDR3-1600 chips and runs at 840 MHz, however I couldn't get it to work with these parameters. Instead Jernej suggested to use a lower clock and adjust some timing parameters, which made it work for me as well.
Many thanks to Jernej Skrabec for his help, also to others who helped with testing and experiments.
The first two patches contain some fixes for the existing driver. Patch 3 moves the existing LPDDR3 timing parameters into a separate file, patch 5 introduces the respective DDR3 timings, patch 6 adds some generic delay lines values. Patch 4 enhances the DRAM controller driver to program DDR3 specific registers as well and use different settings on other registers. The final patch introduces support for the Eachlink H6 Mini TV box, with the usual device tree and defconfig file.
Please have a look and comment!
Cheers, Andre.
I tested this on Tanix TX6 STB, which also has DDR3 and it works well. Patches were also tested on other, LPDDR3 based boards and no regressions were noticed.
Let get this merged soon, I also have follow up patch for half DQ configuration. It's needed for Tanix TX6 mini STB.
Tested-by: Jernej Skrabec jernej.skrabec@siol.net Reviewed-by: Jernej Skrabec jernej.skrabec@siol.net
Best regards, Jernej
Changelog v1..v2:
- name DDR2T mode bit
- explain bonding ID bit
- generalise message about non-supported DRAM types
- update comments about JEDEC values to match DDR3-1333 timings
- use recommended max(ns, cycles) timing formulas where appropriate
- add MAINTAINERS entry for the Eachlink H6 Mini box
Andre Przywara (6): sunxi: H6: DRAM: avoid memcpy() on MMIO registers sunxi: H6: DRAM: follow recommended PHY init algorithm sunxi: H6: move LPDDR3 timing definition into separate file sunxi: H6: Add DDR3 support to DRAM controller driver sunxi: H6: Add DDR3-1333 timings sunxi: H6: Add basic Eachlink H6 Mini support
Jernej Skrabec (1): sunxi: H6: Add DDR3 DRAM delay values
arch/arm/dts/Makefile | 1 + arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts | 116 ++++++++ .../include/asm/arch-sunxi/dram_sun50i_h6.h | 35 +++ arch/arm/mach-sunxi/Kconfig | 18 +- arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/dram_sun50i_h6.c | 251 ++++++------------ arch/arm/mach-sunxi/dram_timings/Makefile | 2 + .../mach-sunxi/dram_timings/h6_ddr3_1333.c | 144 ++++++++++ .../mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 +++++++++ board/sunxi/MAINTAINERS | 6 + configs/eachlink_h6_mini_defconfig | 17 ++ configs/orangepi_one_plus_defconfig | 1 + configs/pine_h64_defconfig | 1 + 13 files changed, 547 insertions(+), 178 deletions(-) create mode 100644 arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c create mode 100644 configs/eachlink_h6_mini_defconfig

On 13/07/2019 10:55, Jernej Škrabec wrote:
Hi,
Dne torek, 02. julij 2019 ob 11:51:41 CEST je Andre Przywara napisal(a):
An updated version, with minor changes. I realised that my comments about JEDEC values were still based on the DDR3-1600 speed bin, so I updated those to match the DDR3-1333 values. I also used the actual JEDEC recommended formulas where that didn't change the actual value written. For more details see the Changelog below.
So far the SPL DRAM driver for the Allwinner H6 SoC only supports LPDDR3 DRAM chips, which are used on most single board computers with this SoC. There are some TV boxes with the H6 out now, but most of them are using DDR3 DRAM instead of LPDDR3.
This series extends the existing H6 DRAM driver to cover DDR3 DRAMs as well. The information used in these patches is from:
- register dumps after Allwinner's boot0 (libdram) has initialised the DRAM
- some disassembly of the libdram library
- timing parameters as found in the boot0 binary
- comparison with Xilinx ZynqMP DRAM controller documentation
The box I played with (Eachlink H6 Mini) has 3GB of DDR3-1600 chips and runs at 840 MHz, however I couldn't get it to work with these parameters. Instead Jernej suggested to use a lower clock and adjust some timing parameters, which made it work for me as well.
Many thanks to Jernej Skrabec for his help, also to others who helped with testing and experiments.
The first two patches contain some fixes for the existing driver. Patch 3 moves the existing LPDDR3 timing parameters into a separate file, patch 5 introduces the respective DDR3 timings, patch 6 adds some generic delay lines values. Patch 4 enhances the DRAM controller driver to program DDR3 specific registers as well and use different settings on other registers. The final patch introduces support for the Eachlink H6 Mini TV box, with the usual device tree and defconfig file.
Please have a look and comment!
Cheers, Andre.
I tested this on Tanix TX6 STB, which also has DDR3 and it works well. Patches were also tested on other, LPDDR3 based boards and no regressions were noticed.
Many thanks for that!
I updated the series to address Icenowy's comment, also adding your tags.
Let get this merged soon, I also have follow up patch for half DQ configuration. It's needed for Tanix TX6 mini STB.
Jagan, can you please push the v3 I just sent, in this merge window, if you don't have any comments or concerns? Thanks!
Cheers, Andre.
Tested-by: Jernej Skrabec jernej.skrabec@siol.net Reviewed-by: Jernej Skrabec jernej.skrabec@siol.net
Best regards, Jernej
Changelog v1..v2:
- name DDR2T mode bit
- explain bonding ID bit
- generalise message about non-supported DRAM types
- update comments about JEDEC values to match DDR3-1333 timings
- use recommended max(ns, cycles) timing formulas where appropriate
- add MAINTAINERS entry for the Eachlink H6 Mini box
Andre Przywara (6): sunxi: H6: DRAM: avoid memcpy() on MMIO registers sunxi: H6: DRAM: follow recommended PHY init algorithm sunxi: H6: move LPDDR3 timing definition into separate file sunxi: H6: Add DDR3 support to DRAM controller driver sunxi: H6: Add DDR3-1333 timings sunxi: H6: Add basic Eachlink H6 Mini support
Jernej Skrabec (1): sunxi: H6: Add DDR3 DRAM delay values
arch/arm/dts/Makefile | 1 + arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts | 116 ++++++++ .../include/asm/arch-sunxi/dram_sun50i_h6.h | 35 +++ arch/arm/mach-sunxi/Kconfig | 18 +- arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/dram_sun50i_h6.c | 251 ++++++------------ arch/arm/mach-sunxi/dram_timings/Makefile | 2 + .../mach-sunxi/dram_timings/h6_ddr3_1333.c | 144 ++++++++++ .../mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 +++++++++ board/sunxi/MAINTAINERS | 6 + configs/eachlink_h6_mini_defconfig | 17 ++ configs/orangepi_one_plus_defconfig | 1 + configs/pine_h64_defconfig | 1 + 13 files changed, 547 insertions(+), 178 deletions(-) create mode 100644 arch/arm/dts/sun50i-h6-eachlink-h6-mini.dts create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c create mode 100644 arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c create mode 100644 configs/eachlink_h6_mini_defconfig
participants (4)
-
Andre Przywara
-
André Przywara
-
Icenowy Zheng
-
Jernej Škrabec