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 <icen...@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, &para->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(&para->ranks[0]), 
> &para->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, &para);
>       mctl_set_cr(socid, &para);
>  
> -     return (1UL << (para.row_bits + para.bank_bits)) * para.page_size *
> -            (para.dual_rank ? 2 : 1);
> +     size = mctl_calc_rank_size(&para.ranks[0]);
> +     if (socid == SOCID_A64 || socid == SOCID_R40) {
> +             if (para.dual_rank)
> +                     size += mctl_calc_rank_size(&para.ranks[1]);
> +     } else if (para.dual_rank) {
> +             size *= 2;
> +     }
> +
> +     return size;
>  }
> 

-- 
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+unsubscr...@googlegroups.com.
To view this discussion on the web, visit 
https://groups.google.com/d/msgid/linux-sunxi/4ecac0ef-20ea-8123-29b9-307e6930676f%40arm.com.

Reply via email to