[U-Boot] [PATCH] sunxi: H6: DRAM: Add support for half DQ

Jernej Škrabec jernej.skrabec at siol.net
Wed Aug 21 06:01:31 UTC 2019


Hi!

Dne sreda, 21. avgust 2019 ob 02:31:04 CEST je André Przywara napisal(a):
> On 17/07/2019 23:16, Jernej Skrabec wrote:
> > Half DQ configuration seems to be very rare for H6 based boards/STBs,
> > but exists nevertheless. Currently the only known product which needs
> > this support is Tanix TX6 mini.
> > 
> > This commit adds support for half DQ configuration. Code was tested
> > for regressions on other configurations (OrangePi 3 1 GiB/LPDDR3, Tanix
> > TX6 4 GiB/DDR3) and none were found.
> > 
> > Thanks to Icenowy Zheng for help with this code.
> > 
> > Signed-off-by: Jernej Skrabec <jernej.skrabec at siol.net>
> 
> I don't have a board with 16-bit DRAM, but can confirm that this still
> works on the PineH64 and the Eachlink H6 Mini.

Thanks for testing!

> 
> Aside from the one question below, the code looks alright for me. I
> verified the bits set or checked below against the Zynq register
> documentation.
> I didn't browse through all of the register documentation to find other
> bits that possibly affect the bus width. But since the code seems to
> work for Jernej, I assume that's fine.

Unfortunately, there is no RSR0 documentation in Zynq register manual, but I 
found this page:
https://git.axiom-project.eu/axiom-evi-qemu/raw/
47171dbf860af6d12c9b82997629460b77378496/hw/misc/xilinx-ddr_phy.c

All RSR0 registers are defined as having QSGERR field, but there is no 
explanation of values it can hold.

BTW, I don't have board with such memory combination, but few people confirmed 
working on Tanix TX6 mini, which it does.

> 
> > ---
> > 
> >  .../include/asm/arch-sunxi/dram_sun50i_h6.h   |  1 +
> >  arch/arm/mach-sunxi/dram_sun50i_h6.c          | 74 ++++++++++++-------
> >  2 files changed, 50 insertions(+), 25 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
> > 0a1da02376..49a8a66f7b 100644
> > --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h
> > +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h
> > @@ -315,6 +315,7 @@ struct dram_para {
> > 
> >  	u8 cols;
> >  	u8 rows;
> >  	u8 ranks;
> > 
> > +	u8 bus_full_width;
> > 
> >  	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];
> >  
> >  };
> > 
> > diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c
> > b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 2a8275da3a..0d65327d35
> > 100644
> > --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
> > +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
> > @@ -201,6 +201,9 @@ static void mctl_set_addrmap(struct dram_para *para)
> > 
> >  	u8 rows = para->rows;
> >  	u8 ranks = para->ranks;
> > 
> > +	if (!para->bus_full_width)
> > +		cols -= 1;
> > +
> > 
> >  	/* Ranks */
> >  	if (ranks == 2)
> >  	
> >  		mctl_ctl->addrmap[0] = rows + cols - 3;
> > 
> > @@ -213,6 +216,10 @@ static void mctl_set_addrmap(struct dram_para *para)
> > 
> >  	/* Columns */
> >  	mctl_ctl->addrmap[2] = 0;
> >  	switch (cols) {
> > 
> > +	case 7:
> > +		mctl_ctl->addrmap[3] = 0x1F1F1F00;
> > +		mctl_ctl->addrmap[4] = 0x1F1F;
> > +		break;
> > 
> >  	case 8:
> >  		mctl_ctl->addrmap[3] = 0x1F1F0000;
> >  		mctl_ctl->addrmap[4] = 0x1F1F;
> > 
> > @@ -300,13 +307,16 @@ static void mctl_com_init(struct dram_para *para)
> > 
> >  		reg_val = 0x3f00;
> >  	
> >  	clrsetbits_le32(&mctl_com->unk_0x008, 0x3f00, reg_val);
> > 
> > -	/* TODO: half DQ, DDR4 */
> > -	reg_val = MSTR_BUSWIDTH_FULL | MSTR_BURST_LENGTH(8) |
> > -		  MSTR_ACTIVE_RANKS(para->ranks);
> > +	/* TODO: DDR4 */
> > +	reg_val = 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;
> > 
> > +	if (para->bus_full_width)
> > +		reg_val |= MSTR_BUSWIDTH_FULL;
> > +	else
> > +		reg_val |= MSTR_BUSWIDTH_HALF;
> > 
> >  	writel(reg_val | BIT(31), &mctl_ctl->mstr);
> >  	
> >  	if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
> > 
> > @@ -333,7 +343,10 @@ static void mctl_com_init(struct dram_para *para)
> > 
> >  	}
> >  	writel(reg_val, &mctl_ctl->odtcfg);
> > 
> > -	/* TODO: half DQ */
> > +	if (!para->bus_full_width) {
> > +		writel(0x0, &mctl_phy->dx[2].gcr[0]);
> > +		writel(0x0, &mctl_phy->dx[3].gcr[0]);
> > +	}
> > 
> >  }
> >  
> >  static void mctl_bit_delay_set(struct dram_para *para)
> > 
> > @@ -514,22 +527,31 @@ static void mctl_channel_init(struct dram_para
> > *para)
> > 
> >  	if (readl(&mctl_phy->pgsr[0]) & 0x400000)
> >  	{
> > 
> > -		/*
> > -		 * Detect single rank.
> > -		 * TODO: also detect half DQ.
> > -		 */
> > +		/* Check for single rank and optionally half DQ. */
> > 
> >  		if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 2 &&
> > 
> > -		    (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2 &&
> > -		    (readl(&mctl_phy->dx[2].rsr[0]) & 0x3) == 2 &&
> > -		    (readl(&mctl_phy->dx[3].rsr[0]) & 0x3) == 2) {
> > +		    (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2) {
> > 
> >  			para->ranks = 1;
> > 
> > +
> > +			if ((readl(&mctl_phy->dx[2].rsr[0]) & 0x3) !
= 2 ||
> > +			    (readl(&mctl_phy->dx[3].rsr[0]) & 0x3) !
= 2)
> > +				para->bus_full_width = 0;
> > +
> > 
> >  			/* Restart DRAM initialization from scratch. 
*/
> >  			mctl_core_init(para);
> >  			return;
> >  		
> >  		}
> > 
> > -		else {
> > -			panic("This DRAM setup is currently not 
supported.\n");
> > +
> > +		/* Check for dual rank and half DQ */
> > +		if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 0 &&
> > +		    (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 0) {
> > +			para->bus_full_width = 0;
> 
> Mmh, aren't those bits always supposed to be 0 here, regardless of the
> bus width? So we just confirm dual rank here? Can we actually say from
> those error bits whether no errors in bits 16-31 mean them being masked
> or whether that means the upper 16 bits are working fine?
> Or is bus_fill_width always 1 at this point? But then we would need to
> check dx[2].rsr[0] and dx[3].rsr[0], wouldn't we?

As I already pointed out, I couldn't found useful documentation of RSR0, so I 
checked what libdram does and put similar check here too. They always check 
only two bits in all RSR0 registers. I suppose 1 and 3 can be read from 
registers or register values may not be same. But I really have no idea what 
they mean. I would be glad if anyone can provide more information.

That being said, I see what bothers you. I checked libdram logic again and 
unless I missed anything, this is exactly the check libdram has. However, 
because having dual rank and half DQ seems unlikely, we may skip this block 
altogether, but that would require another round of tests.

Best regards,
Jernej

> 
> Cheers,
> Andre.
> 
> > +
> > +			/* Restart DRAM initialization from scratch. 
*/
> > +			mctl_core_init(para);
> > +			return;
> > 
> >  		}
> > 
> > +
> > +		panic("This DRAM setup is currently not supported.\n");
> > 
> >  	}
> >  	
> >  	if (readl(&mctl_phy->pgsr[0]) & 0xff00000) {
> > 
> > @@ -557,11 +579,8 @@ static void mctl_channel_init(struct dram_para *para)
> > 
> >  static void mctl_auto_detect_dram_size(struct dram_para *para)
> >  {
> > 
> > -	/* TODO: non-LPDDR3, half DQ */
> > -	/*
> > -	 * Detect rank number by the code in mctl_channel_init. Furtherly
> > -	 * when DQ detection is available it will also be executed there.
> > -	 */
> > +	/* TODO: non-(LP)DDR3 */
> > +	/* Detect rank number and half DQ by the code in 
mctl_channel_init. */
> > 
> >  	mctl_core_init(para);
> >  	
> >  	/* detect row address bits */
> > 
> > @@ -570,8 +589,9 @@ static void mctl_auto_detect_dram_size(struct
> > dram_para *para)> 
> >  	mctl_core_init(para);
> >  	
> >  	for (para->rows = 13; para->rows < 18; para->rows++) {
> > 
> > -		/* 8 banks, 8 bit per byte and 32 bit width */
> > -		if (mctl_mem_matches((1 << (para->rows + para->cols + 
5))))
> > +		/* 8 banks, 8 bit per byte and 16/32 bit width */
> > +		if (mctl_mem_matches((1 << (para->rows + para->cols +
> > +					    4 + para-
>bus_full_width))))
> > 
> >  			break;
> >  	
> >  	}
> > 
> > @@ -580,18 +600,21 @@ static void mctl_auto_detect_dram_size(struct
> > dram_para *para)> 
> >  	mctl_core_init(para);
> >  	
> >  	for (para->cols = 8; para->cols < 11; para->cols++) {
> > 
> > -		/* 8 bits per byte and 32 bit width */
> > -		if (mctl_mem_matches(1 << (para->cols + 2)))
> > +		/* 8 bits per byte and 16/32 bit width */
> > +		if (mctl_mem_matches(1 << (para->cols + 1 +
> > +					   para-
>bus_full_width)))
> > 
> >  			break;
> >  	
> >  	}
> >  
> >  }
> >  
> >  unsigned long mctl_calc_size(struct dram_para *para)
> >  {
> > 
> > -	/* TODO: non-LPDDR3, half DQ */
> > +	u8 width = para->bus_full_width ? 4 : 2;
> > +
> > +	/* TODO: non-(LP)DDR3 */
> > 
> > -	/* 8 banks, 32-bit (4 byte) data width */
> > -	return (1ULL << (para->cols + para->rows + 3)) * 4 * para->ranks;
> > +	/* 8 banks */
> > +	return (1ULL << (para->cols + para->rows + 3)) * width * para-
>ranks;
> > 
> >  }
> >  
> >  #define SUN50I_H6_LPDDR3_DX_WRITE_DELAYS			\
> > 
> > @@ -625,6 +648,7 @@ unsigned long sunxi_dram_init(void)
> > 
> >  		.ranks = 2,
> >  		.cols = 11,
> >  		.rows = 14,
> > 
> > +		.bus_full_width = 1,
> > 
> >  #ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3
> >  
> >  		.type = SUNXI_DRAM_TYPE_LPDDR3,
> >  		.dx_read_delays  = SUN50I_H6_LPDDR3_DX_READ_DELAYS,






More information about the U-Boot mailing list