All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support
@ 2019-06-19  1:11 Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 1/7] sunxi: H6: DRAM: avoid memcpy() on MMIO registers Andre Przywara
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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.

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 +++++++++++
 arch/arm/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              | 240 +++++++---------------
 arch/arm/mach-sunxi/dram_timings/Makefile         |   2 +
 arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c   | 144 +++++++++++++
 arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 ++++++++++++
 configs/eachlink_h6_mini_defconfig                |  17 ++
 configs/orangepi_one_plus_defconfig               |   1 +
 configs/pine_h64_defconfig                        |   1 +
 12 files changed, 537 insertions(+), 171 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

-- 
2.14.5

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 1/7] sunxi: H6: DRAM: avoid memcpy() on MMIO registers
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 2/7] sunxi: H6: DRAM: follow recommended PHY init algorithm Andre Przywara
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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,
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 2/7] sunxi: H6: DRAM: follow recommended PHY init algorithm
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 1/7] sunxi: H6: DRAM: avoid memcpy() on MMIO registers Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 3/7] sunxi: H6: move LPDDR3 timing definition into separate file Andre Przywara
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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));
 }
 
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 3/7] sunxi: H6: move LPDDR3 timing definition into separate file
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 1/7] sunxi: H6: DRAM: avoid memcpy() on MMIO registers Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 2/7] sunxi: H6: DRAM: follow recommended PHY init algorithm Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 4/7] sunxi: H6: Add DDR3 support to DRAM controller driver Andre Przywara
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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>
---
 arch/arm/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 +
 arch/arm/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
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 4/7] sunxi: H6: Add DDR3 support to DRAM controller driver
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
                   ` (2 preceding siblings ...)
  2019-06-19  1:11 ` [U-Boot] [PATCH 3/7] sunxi: H6: move LPDDR3 timing definition into separate file Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19 15:54   ` [U-Boot] [linux-sunxi] " Jernej Škrabec
  2019-06-19  1:11 ` [U-Boot] [PATCH 5/7] sunxi: H6: Add DDR3-1333 timings Andre Przywara
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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>
---
 arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h |  7 +++
 arch/arm/mach-sunxi/dram_sun50i_h6.c             | 71 +++++++++++++++++-------
 2 files changed, 57 insertions(+), 21 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..8b8085611f 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.
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index 697b8af4ce..0436265bdb 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:
@@ -302,22 +303,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 | BIT(28); /* 2T mode */
+	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 if (para->type == SUNXI_DRAM_TYPE_DDR4) {
+		panic("DDR4 not yet supported\n");
+	}
 	writel(reg_val, &mctl_ctl->odtcfg);
 
 	/* TODO: half DQ */
@@ -372,6 +388,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 +438,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 +448,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 +482,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 +544,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 +614,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
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 5/7] sunxi: H6: Add DDR3-1333 timings
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
                   ` (3 preceding siblings ...)
  2019-06-19  1:11 ` [U-Boot] [PATCH 4/7] sunxi: H6: Add DDR3 support to DRAM controller driver Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19 16:05   ` Jernej Škrabec
  2019-06-19  1:11 ` [U-Boot] [PATCH 6/7] sunxi: H6: Add DDR3 DRAM delay values Andre Przywara
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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 +
 arch/arm/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..12de4db310
--- /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: 40 ns */
+	u8 trrd		= max(ns_to_t(10), 2);	/* JEDEC: max(7.5 ns, 4nCK) */
+	u8 trcd		= ns_to_t(15);		/* JEDEC: 13.75 ns */
+	u8 trc		= ns_to_t(53);		/* JEDEC: 48.75 ns */
+	u8 txp		= max(ns_to_t(8), 2);	/* 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		= max(ns_to_t(15), 2);	/* ? */
+	u8 trp		= ns_to_t(15);		/* JEDEC: >= 13.75 ns */
+	u8 tras		= ns_to_t(38);		/* JEDEC >= 35 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;
+	u8 tmod		= 12;
+	u8 tcke		= 3;
+	u8 tcksrx	= 5;
+	u8 tcksre	= 5;
+	u8 tckesr	= tcke + 1;
+	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: 11 / 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);
+}
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 6/7] sunxi: H6: Add DDR3 DRAM delay values
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
                   ` (4 preceding siblings ...)
  2019-06-19  1:11 ` [U-Boot] [PATCH 5/7] sunxi: H6: Add DDR3-1333 timings Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19  1:11 ` [U-Boot] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support Andre Przywara
  2019-06-22 22:28 ` [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support hex dump
  7 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

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 0436265bdb..5fe53bf463 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -597,17 +597,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 =
@@ -619,12 +630,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
 	};
 
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
                   ` (5 preceding siblings ...)
  2019-06-19  1:11 ` [U-Boot] [PATCH 6/7] sunxi: H6: Add DDR3 DRAM delay values Andre Przywara
@ 2019-06-19  1:11 ` Andre Przywara
  2019-06-19  9:03   ` [U-Boot] [linux-sunxi] " Clément Péron
  2019-06-22 22:28 ` [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support hex dump
  7 siblings, 1 reply; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  1:11 UTC (permalink / raw)
  To: u-boot

The Eachlink H6 Mini is a modestly priced TV box, using the Allwinner H6
SoC. It comes with 4GB of DRAM (3GB usable) 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 ++++++++++++++++++++++++++++
 configs/eachlink_h6_mini_defconfig          |  17 ++++
 3 files changed, 134 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 528fb909d5..c463aca190 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -507,6 +507,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 = <&reg_vcc3v3>;
+	cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&mmc2 {
+	vmmc-supply = <&reg_vcc3v3>;
+	vqmmc-supply = <&reg_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 = <&reg_vcc5v>;
+	status = "okay";
+};
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"
-- 
2.14.5

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [U-Boot] [linux-sunxi] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support
  2019-06-19  1:11 ` [U-Boot] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support Andre Przywara
@ 2019-06-19  9:03   ` Clément Péron
  2019-06-19  9:24     ` Andre Przywara
  0 siblings, 1 reply; 13+ messages in thread
From: Clément Péron @ 2019-06-19  9:03 UTC (permalink / raw)
  To: u-boot

Hi Andre,

On Wed, 19 Jun 2019 at 03:12, Andre Przywara <andre.przywara@arm.com> wrote:
>
> The Eachlink H6 Mini is a modestly priced TV box, using the Allwinner H6
> SoC. It comes with 4GB of DRAM (3GB usable) 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 ++++++++++++++++++++++++++++
>  configs/eachlink_h6_mini_defconfig          |  17 ++++
>  3 files changed, 134 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 528fb909d5..c463aca190 100644
> --- a/arch/arm/dts/Makefile
> +++ b/arch/arm/dts/Makefile
> @@ -507,6 +507,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";
> +};

Same as Pine H64. If you plan to mainline this board in Linux maybe
you should try to stuck with the linux one to avoid any differences.

> +
> +&ehci3 {
> +       status = "okay";
> +};
> +
> +&mmc0 {
> +       vmmc-supply = <&reg_vcc3v3>;
> +       cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
> +       bus-width = <4>;
> +       status = "okay";
> +};
> +
> +&mmc2 {
> +       vmmc-supply = <&reg_vcc3v3>;
> +       vqmmc-supply = <&reg_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 = <&reg_vcc5v>;
> +       status = "okay";
> +};
> 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
This is now the default and it's no more required.

> +# 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"

You should also add yourself in the MAINTAINERS file for the dts and
config file of the board.

Regards,
Clément

> --
> 2.14.5
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/20190619011109.2080-8-andre.przywara%40arm.com.
> For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [U-Boot] [linux-sunxi] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support
  2019-06-19  9:03   ` [U-Boot] [linux-sunxi] " Clément Péron
@ 2019-06-19  9:24     ` Andre Przywara
  0 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2019-06-19  9:24 UTC (permalink / raw)
  To: u-boot

On Wed, 19 Jun 2019 11:03:25 +0200
Clément Péron <peron.clem@gmail.com> wrote:

Hi Clément,

> On Wed, 19 Jun 2019 at 03:12, Andre Przywara <andre.przywara@arm.com> wrote:
> >
> > The Eachlink H6 Mini is a modestly priced TV box, using the Allwinner H6
> > SoC. It comes with 4GB of DRAM (3GB usable) 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 ++++++++++++++++++++++++++++
> >  configs/eachlink_h6_mini_defconfig          |  17 ++++
> >  3 files changed, 134 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 528fb909d5..c463aca190 100644
> > --- a/arch/arm/dts/Makefile
> > +++ b/arch/arm/dts/Makefile
> > @@ -507,6 +507,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";
> > +};  
> 
> Same as Pine H64. If you plan to mainline this board in Linux maybe
> you should try to stuck with the linux one to avoid any differences.

Well, there is no DT in Linux yet, so how could I deviate?
In fact I will send an ideally identical .dts to Linux later this week, so
we have this bit in from the beginning.

I think it's a bit of a chicken and egg problem: I can't really post Linux
support patches without having U-Boot running, so have to start somewhere.

> > +
> > +&ehci3 {
> > +       status = "okay";
> > +};
> > +
> > +&mmc0 {
> > +       vmmc-supply = <&reg_vcc3v3>;
> > +       cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
> > +       bus-width = <4>;
> > +       status = "okay";
> > +};
> > +
> > +&mmc2 {
> > +       vmmc-supply = <&reg_vcc3v3>;
> > +       vqmmc-supply = <&reg_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 = <&reg_vcc5v>;
> > +       status = "okay";
> > +};
> > 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  
> This is now the default and it's no more required.

I know, but it's not in current mainline, which this patch is against.

> > +# 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"  
> 
> You should also add yourself in the MAINTAINERS file for the dts and
> config file of the board.

Sure.

Thanks for having a look!

Cheers,
Andre.

> > --
> > 2.14.5
> >
> > --
> > You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> > To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
> > To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/20190619011109.2080-8-andre.przywara%40arm.com.
> > For more options, visit https://groups.google.com/d/optout.  

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [U-Boot] [linux-sunxi] [PATCH 4/7] sunxi: H6: Add DDR3 support to DRAM controller driver
  2019-06-19  1:11 ` [U-Boot] [PATCH 4/7] sunxi: H6: Add DDR3 support to DRAM controller driver Andre Przywara
@ 2019-06-19 15:54   ` Jernej Škrabec
  0 siblings, 0 replies; 13+ messages in thread
From: Jernej Škrabec @ 2019-06-19 15:54 UTC (permalink / raw)
  To: u-boot

Hi!

Dne sreda, 19. junij 2019 ob 03:11:06 CEST je Andre Przywara napisal(a):
> 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>
> ---
>  arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h |  7 +++
>  arch/arm/mach-sunxi/dram_sun50i_h6.c             | 71
> +++++++++++++++++------- 2 files changed, 57 insertions(+), 21 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..8b8085611f 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.
> diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c
> b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 697b8af4ce..0436265bdb 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:
> @@ -302,22 +303,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 | BIT(28); /* 2T mode 
*/

You should make a define for BIT(28).

> +	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 if (para->type == SUNXI_DRAM_TYPE_DDR4) {
> +		panic("DDR4 not yet supported\n");

If we go this route, you should also add LPDDR2 or just generalize this 
message and remove "if".

Best regards,
Jernej

> +	}
>  	writel(reg_val, &mctl_ctl->odtcfg);
> 
>  	/* TODO: half DQ */
> @@ -372,6 +388,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 +438,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 +448,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 +482,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 +544,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 +614,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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 5/7] sunxi: H6: Add DDR3-1333 timings
  2019-06-19  1:11 ` [U-Boot] [PATCH 5/7] sunxi: H6: Add DDR3-1333 timings Andre Przywara
@ 2019-06-19 16:05   ` Jernej Škrabec
  0 siblings, 0 replies; 13+ messages in thread
From: Jernej Škrabec @ 2019-06-19 16:05 UTC (permalink / raw)
  To: u-boot

Hi!

Dne sreda, 19. junij 2019 ob 03:11:07 CEST je Andre Przywara napisal(a):
> 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 +
>  arch/arm/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..12de4db310
> --- /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: 40 ns */
> +	u8 trrd		= max(ns_to_t(10), 2);	/* JEDEC: max(7.5 
ns, 4nCK) */
> +	u8 trcd		= ns_to_t(15);		/* 
JEDEC: 13.75 ns */
> +	u8 trc		= ns_to_t(53);		/* 
JEDEC: 48.75 ns */
> +	u8 txp		= max(ns_to_t(8), 2);	/* 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		= max(ns_to_t(15), 2);	/* ? */
> +	u8 trp		= ns_to_t(15);		/* 
JEDEC: >= 13.75 ns */
> +	u8 tras		= ns_to_t(38);		/* JEDEC 
>= 35 ns, <= 9*trefi */
> +	u8 twtr_sa	= 2;			/* ? */
> +	u8 tcksrea	= 4;			/* ? */
> +	u16 trefi	= ns_to_t(7800) / 32;	/* JEDEC: 7.8us at Tcase <= 85C 
*/
> +	u16 trfc	= ns_to_t(350);		/* JEDEC: 160 ns 
for 2Gb */
> +	u16 txsr	= 4;			/* ? */
> +
> +	u8 tmrw		= 0;			/* ? */
> +	u8 tmrd		= 4;
> +	u8 tmod		= 12;
> +	u8 tcke		= 3;
> +	u8 tcksrx	= 5;
> +	u8 tcksre	= 5;
> +	u8 tckesr	= tcke + 1;
> +	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: 
11 / 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 */

Your original formula from WIP branch (tcl + 2 + 1 - tcwl) gives correct value 
here, so I would use that instead of a fixed value. However, it's not clear how 
you derived that from the formula in the comment. Please add better 
explanation for all three calculations.

In general I noticed that there is just slight difference between value 
specified in JEDEC standard and that used by libdram for some parameters. I 
think it would be better to use JEDEC value if that doesn't break anything.

Best regards,
Jernej

> +
> +	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);
> +}

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support
  2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
                   ` (6 preceding siblings ...)
  2019-06-19  1:11 ` [U-Boot] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support Andre Przywara
@ 2019-06-22 22:28 ` hex dump
  7 siblings, 0 replies; 13+ messages in thread
From: hex dump @ 2019-06-22 22:28 UTC (permalink / raw)
  To: u-boot

On Fri, Jun 21, 2019 at 3:54 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> 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.
>
> 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 +++++++++++
>  arch/arm/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              | 240 +++++++---------------
>  arch/arm/mach-sunxi/dram_timings/Makefile         |   2 +
>  arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c   | 144 +++++++++++++
>  arch/arm/mach-sunxi/dram_timings/h6_lpddr3_1333.c | 132 ++++++++++++
>  configs/eachlink_h6_mini_defconfig                |  17 ++
>  configs/orangepi_one_plus_defconfig               |   1 +
>  configs/pine_h64_defconfig                        |   1 +
>  12 files changed, 537 insertions(+), 171 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
>
> --
> 2.14.5
>
Tested-by: hexdump <hexdump0815@googlemail.com>

BEFORE (no patches from this series applied):
my qplus (Q+) h6 tv box (seems to be the same hardware as the eachlink
h6 mini of andre) did hang early on in the spl during the memory setup
with mainline uboot

AFTER (the patch applied):
my qplus (Q+) h6 tv box boots perfectly fine with mainline uboot - no
more problems with the memory setup - thanks a lot for this patrch
series

best wishes - hexdump

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-06-22 22:28 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-19  1:11 [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support Andre Przywara
2019-06-19  1:11 ` [U-Boot] [PATCH 1/7] sunxi: H6: DRAM: avoid memcpy() on MMIO registers Andre Przywara
2019-06-19  1:11 ` [U-Boot] [PATCH 2/7] sunxi: H6: DRAM: follow recommended PHY init algorithm Andre Przywara
2019-06-19  1:11 ` [U-Boot] [PATCH 3/7] sunxi: H6: move LPDDR3 timing definition into separate file Andre Przywara
2019-06-19  1:11 ` [U-Boot] [PATCH 4/7] sunxi: H6: Add DDR3 support to DRAM controller driver Andre Przywara
2019-06-19 15:54   ` [U-Boot] [linux-sunxi] " Jernej Škrabec
2019-06-19  1:11 ` [U-Boot] [PATCH 5/7] sunxi: H6: Add DDR3-1333 timings Andre Przywara
2019-06-19 16:05   ` Jernej Škrabec
2019-06-19  1:11 ` [U-Boot] [PATCH 6/7] sunxi: H6: Add DDR3 DRAM delay values Andre Przywara
2019-06-19  1:11 ` [U-Boot] [PATCH 7/7] sunxi: H6: Add basic Eachlink H6 Mini support Andre Przywara
2019-06-19  9:03   ` [U-Boot] [linux-sunxi] " Clément Péron
2019-06-19  9:24     ` Andre Przywara
2019-06-22 22:28 ` [U-Boot] [PATCH 0/7] sunxi: Add H6 DDR3 DRAM support hex dump

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.