Message ID | 20210220121416.15215-3-nicolas@debian.org |
---|---|
State | Superseded |
Delegated to: | Andre Przywara |
Headers | show |
Series | sunxi: support asymmetric dual rank DRAM on A64/R40 | expand |
On 20/02/2021 12:14, Nicolas Boulenguez wrote: Hi, > From: Icenowy Zheng <icenowy@aosc.io> > > 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. > > 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. Icenowy just sent a new version, together with another patch. I will review her version, so you can drop this one here. Cheers, Andre > > Signed-off-by: Icenowy Zheng <icenowy@aosc.io> > --- > .../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 85e7a1874e..b679f92e70 100644 > --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c > +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c > @@ -346,18 +346,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); > } > } > > @@ -581,35 +587,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 > @@ -688,12 +722,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, > @@ -762,6 +807,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; > } >
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 85e7a1874e..b679f92e70 100644 --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c @@ -346,18 +346,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); } } @@ -581,35 +587,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 @@ -688,12 +722,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, @@ -762,6 +807,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; }