于 2019年7月2日 GMT+08:00 下午5:51:44, Andre Przywara <andre.przyw...@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.przyw...@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 <icen...@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 -- 使用 K-9 Mail 发送自我的Android设备。 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot