[RFC PATCH] sunxi: support asymmetric dual rank DRAM on A64/R40
André Przywara
andre.przywara at arm.com
Wed Jun 24 02:28:39 CEST 2020
On 19/06/2020 13:16, Icenowy Zheng wrote:
Hi Icenowy,
> Previously we have known that R40 has a configuration register for its
> rank 1, which allows different configuration than rank 0. Reverse
> engineering of newest libdram of A64 from Allwinner shows that A64 has
> this register too. It's bit 0 (which enables dual rank in rank 0
> configuration register) means a dedicated rank size setup is used for
> rank 1.
Ah! That's a nice discovery, as it probably explains how my Eachlink H6
TV box works with its asymmetric 3GB DRAM config!
> Now, Pine64 scheduled to use a 3GiB LPDDR3 DRAM chip (which has 2GiB
> rank 0 and 1GiB rank 1) on PinePhone, that makes asymmetric dual rank
> DRAM support necessary.
>
> Add this support. As we have gained knowledge of asymmetric dual rank,
> we can now allow R40 dual rank memory setup to work too.
>
> Signed-off-by: Icenowy Zheng <icenowy at aosc.io>
> ---
> Testing on R40 boards and A64 single rank boards (e.g. the original Pine
> A64+) is welcomed. I have these boards, but I get too lazy to take them
> out and test them.
I briefly tested this on a Bananapi M2 Berry (R40, 1GB), Pine64 512MB
and Pine64+ 2GB. All three boot happily into U-Boot, tested the Pine64+
to the Linux prompt as well.
I will do a proper review shortly.
Thanks for the patch!
Cheers,
Andre
>
> .../include/asm/arch-sunxi/dram_sunxi_dw.h | 11 +-
> arch/arm/mach-sunxi/dram_sunxi_dw.c | 100 +++++++++++++-----
> 2 files changed, 84 insertions(+), 27 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h b/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
> index a5a7ebde44..e843c14202 100644
> --- a/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
> +++ b/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
> @@ -215,12 +215,17 @@ struct sunxi_mctl_ctl_reg {
> #define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE)
> /* The eight data lines (DQn) plus DM, DQS and DQSN */
> #define LINES_PER_BYTE_LANE (BITS_PER_BYTE + 3)
> -struct dram_para {
> +
> +struct rank_para {
> u16 page_size;
> - u8 bus_full_width;
> - u8 dual_rank;
> u8 row_bits;
> u8 bank_bits;
> +};
> +
> +struct dram_para {
> + u8 dual_rank;
> + u8 bus_full_width;
> + struct rank_para ranks[2];
> const u8 dx_read_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
> const u8 dx_write_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
> const u8 ac_delays[31];
> diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c
> index a462538521..7a40d92349 100644
> --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c
> +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c
> @@ -349,18 +349,24 @@ static void mctl_set_cr(uint16_t socid, struct dram_para *para)
> #else
> #error Unsupported DRAM type!
> #endif
> - (para->bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
> + (para->ranks[0].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
> MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
> (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
> - MCTL_CR_PAGE_SIZE(para->page_size) |
> - MCTL_CR_ROW_BITS(para->row_bits), &mctl_com->cr);
> + MCTL_CR_PAGE_SIZE(para->ranks[0].page_size) |
> + MCTL_CR_ROW_BITS(para->ranks[0].row_bits), &mctl_com->cr);
>
> - if (socid == SOCID_R40) {
> - if (para->dual_rank)
> - panic("Dual rank memory not supported\n");
> + if (socid == SOCID_A64 || socid == SOCID_R40) {
> + writel((para->ranks[1].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
> + MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
> + (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
> + MCTL_CR_PAGE_SIZE(para->ranks[1].page_size) |
> + MCTL_CR_ROW_BITS(para->ranks[1].row_bits), &mctl_com->cr_r1);
> + }
>
> + if (socid == SOCID_R40) {
> /* Mux pin to A15 address line for single rank memory. */
> - setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
> + if (!para->dual_rank)
> + setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
> }
> }
>
> @@ -584,35 +590,63 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para)
> return 0;
> }
>
> -static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
> +/*
> + * Test if memory at offset offset matches memory at a certain base
> + */
> +static bool mctl_mem_matches_base(u32 offset, ulong base)
> +{
> + /* Try to write different values to RAM at two addresses */
> + writel(0, base);
> + writel(0xaa55aa55, base + offset);
> + dsb();
> + /* Check if the same value is actually observed when reading back */
> + return readl(base) ==
> + readl(base + offset);
> +}
> +
> +static void mctl_auto_detect_dram_size_rank(uint16_t socid, struct dram_para *para, ulong base, struct rank_para *rank)
> {
> /* detect row address bits */
> - para->page_size = 512;
> - para->row_bits = 16;
> - para->bank_bits = 2;
> + rank->page_size = 512;
> + rank->row_bits = 16;
> + rank->bank_bits = 2;
> mctl_set_cr(socid, para);
>
> - for (para->row_bits = 11; para->row_bits < 16; para->row_bits++)
> - if (mctl_mem_matches((1 << (para->row_bits + para->bank_bits)) * para->page_size))
> + for (rank->row_bits = 11; rank->row_bits < 16; rank->row_bits++)
> + if (mctl_mem_matches_base((1 << (rank->row_bits + rank->bank_bits)) * rank->page_size, base))
> break;
>
> /* detect bank address bits */
> - para->bank_bits = 3;
> + rank->bank_bits = 3;
> mctl_set_cr(socid, para);
>
> - for (para->bank_bits = 2; para->bank_bits < 3; para->bank_bits++)
> - if (mctl_mem_matches((1 << para->bank_bits) * para->page_size))
> + for (rank->bank_bits = 2; rank->bank_bits < 3; rank->bank_bits++)
> + if (mctl_mem_matches_base((1 << rank->bank_bits) * rank->page_size, base))
> break;
>
> /* detect page size */
> - para->page_size = 8192;
> + rank->page_size = 8192;
> mctl_set_cr(socid, para);
>
> - for (para->page_size = 512; para->page_size < 8192; para->page_size *= 2)
> - if (mctl_mem_matches(para->page_size))
> + for (rank->page_size = 512; rank->page_size < 8192; rank->page_size *= 2)
> + if (mctl_mem_matches_base(rank->page_size, base))
> break;
> }
>
> +static unsigned long mctl_calc_rank_size(struct rank_para *rank)
> +{
> + return (1UL << (rank->row_bits + rank->bank_bits)) * rank->page_size;
> +}
> +
> +static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
> +{
> + mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE, ¶->ranks[0]);
> +
> + if ((socid == SOCID_A64 || socid == SOCID_R40) && para->dual_rank) {
> + mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE + mctl_calc_rank_size(¶->ranks[0]), ¶->ranks[1]);
> + }
> +}
> +
> /*
> * The actual values used here are taken from Allwinner provided boot0
> * binaries, though they are probably board specific, so would likely benefit
> @@ -691,12 +725,23 @@ unsigned long sunxi_dram_init(void)
> struct sunxi_mctl_ctl_reg * const mctl_ctl =
> (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
>
> + unsigned long size;
> +
> struct dram_para para = {
> .dual_rank = 1,
> .bus_full_width = 1,
> - .row_bits = 15,
> - .bank_bits = 3,
> - .page_size = 4096,
> + .ranks = {
> + {
> + .row_bits = 15,
> + .bank_bits = 3,
> + .page_size = 4096,
> + },
> + {
> + .row_bits = 15,
> + .bank_bits = 3,
> + .page_size = 4096,
> + }
> + },
>
> #if defined(CONFIG_MACH_SUN8I_H3)
> .dx_read_delays = SUN8I_H3_DX_READ_DELAYS,
> @@ -765,6 +810,13 @@ unsigned long sunxi_dram_init(void)
> mctl_auto_detect_dram_size(socid, ¶);
> mctl_set_cr(socid, ¶);
>
> - return (1UL << (para.row_bits + para.bank_bits)) * para.page_size *
> - (para.dual_rank ? 2 : 1);
> + size = mctl_calc_rank_size(¶.ranks[0]);
> + if (socid == SOCID_A64 || socid == SOCID_R40) {
> + if (para.dual_rank)
> + size += mctl_calc_rank_size(¶.ranks[1]);
> + } else if (para.dual_rank) {
> + size *= 2;
> + }
> +
> + return size;
> }
>
More information about the U-Boot
mailing list