443 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * (C) Copyright 2018 Rockchip Electronics Co., Ltd.
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <debug_uart.h>
 | |
| #include <ram.h>
 | |
| #include <asm/io.h>
 | |
| #include <asm/arch-rockchip/sdram.h>
 | |
| #include <asm/arch-rockchip/sdram_common.h>
 | |
| 
 | |
| #ifdef CONFIG_RAM_ROCKCHIP_DEBUG
 | |
| void sdram_print_dram_type(unsigned char dramtype)
 | |
| {
 | |
| 	switch (dramtype) {
 | |
| 	case DDR3:
 | |
| 		printascii("DDR3");
 | |
| 		break;
 | |
| 	case DDR4:
 | |
| 		printascii("DDR4");
 | |
| 		break;
 | |
| 	case LPDDR2:
 | |
| 		printascii("LPDDR2");
 | |
| 		break;
 | |
| 	case LPDDR3:
 | |
| 		printascii("LPDDR3");
 | |
| 		break;
 | |
| 	case LPDDR4:
 | |
| 		printascii("LPDDR4");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printascii("Unknown Device");
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void sdram_print_ddr_info(struct sdram_cap_info *cap_info,
 | |
| 			  struct sdram_base_params *base)
 | |
| {
 | |
| 	u64 cap;
 | |
| 	u32 bg;
 | |
| 
 | |
| 	bg = (cap_info->dbw == 0) ? 2 : 1;
 | |
| 
 | |
| 	sdram_print_dram_type(base->dramtype);
 | |
| 
 | |
| 	printascii(", ");
 | |
| 	printdec(base->ddr_freq);
 | |
| 	printascii("MHz\n");
 | |
| 
 | |
| 	printascii("BW=");
 | |
| 	printdec(8 << cap_info->bw);
 | |
| 	printascii(" Col=");
 | |
| 	printdec(cap_info->col);
 | |
| 	printascii(" Bk=");
 | |
| 	printdec(0x1 << cap_info->bk);
 | |
| 	if (base->dramtype == DDR4) {
 | |
| 		printascii(" BG=");
 | |
| 		printdec(1 << bg);
 | |
| 	}
 | |
| 	printascii(" CS0 Row=");
 | |
| 	printdec(cap_info->cs0_row);
 | |
| 	if (cap_info->cs0_high16bit_row !=
 | |
| 		cap_info->cs0_row) {
 | |
| 		printascii("/");
 | |
| 		printdec(cap_info->cs0_high16bit_row);
 | |
| 	}
 | |
| 	if (cap_info->rank > 1) {
 | |
| 		printascii(" CS1 Row=");
 | |
| 		printdec(cap_info->cs1_row);
 | |
| 		if (cap_info->cs1_high16bit_row !=
 | |
| 			cap_info->cs1_row) {
 | |
| 			printascii("/");
 | |
| 			printdec(cap_info->cs1_high16bit_row);
 | |
| 		}
 | |
| 	}
 | |
| 	printascii(" CS=");
 | |
| 	printdec(cap_info->rank);
 | |
| 	printascii(" Die BW=");
 | |
| 	printdec(8 << cap_info->dbw);
 | |
| 
 | |
| 	cap = sdram_get_cs_cap(cap_info, 3, base->dramtype);
 | |
| 	if (cap_info->row_3_4)
 | |
| 		cap = cap * 3 / 4;
 | |
| 
 | |
| 	printascii(" Size=");
 | |
| 	printdec(cap >> 20);
 | |
| 	printascii("MB\n");
 | |
| }
 | |
| 
 | |
| void sdram_print_stride(unsigned int stride)
 | |
| {
 | |
| 	switch (stride) {
 | |
| 	case 0xc:
 | |
| 		printf("128B stride\n");
 | |
| 		break;
 | |
| 	case 5:
 | |
| 	case 9:
 | |
| 	case 0xd:
 | |
| 	case 0x11:
 | |
| 	case 0x19:
 | |
| 		printf("256B stride\n");
 | |
| 		break;
 | |
| 	case 0xa:
 | |
| 	case 0xe:
 | |
| 	case 0x12:
 | |
| 		printf("512B stride\n");
 | |
| 		break;
 | |
| 	case 0xf:
 | |
| 		printf("4K stride\n");
 | |
| 		break;
 | |
| 	case 0x1f:
 | |
| 		printf("32MB + 256B stride\n");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("no stride\n");
 | |
| 	}
 | |
| }
 | |
| #else
 | |
| inline void sdram_print_dram_type(unsigned char dramtype)
 | |
| {
 | |
| }
 | |
| 
 | |
| inline void sdram_print_ddr_info(struct sdram_cap_info *cap_info,
 | |
| 				 struct sdram_base_params *base)
 | |
| {
 | |
| }
 | |
| 
 | |
| inline void sdram_print_stride(unsigned int stride)
 | |
| {
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * cs: 0:cs0
 | |
|  *	   1:cs1
 | |
|  *     else cs0+cs1
 | |
|  * note: it didn't consider about row_3_4
 | |
|  */
 | |
| u64 sdram_get_cs_cap(struct sdram_cap_info *cap_info, u32 cs, u32 dram_type)
 | |
| {
 | |
| 	u32 bg;
 | |
| 	u64 cap[2];
 | |
| 
 | |
| 	if (dram_type == DDR4)
 | |
| 		/* DDR4 8bit dram BG = 2(4bank groups),
 | |
| 		 * 16bit dram BG = 1 (2 bank groups)
 | |
| 		 */
 | |
| 		bg = (cap_info->dbw == 0) ? 2 : 1;
 | |
| 	else
 | |
| 		bg = 0;
 | |
| 	cap[0] = 1llu << (cap_info->bw + cap_info->col +
 | |
| 		bg + cap_info->bk + cap_info->cs0_row);
 | |
| 
 | |
| 	if (cap_info->rank == 2)
 | |
| 		cap[1] = 1llu << (cap_info->bw + cap_info->col +
 | |
| 			bg + cap_info->bk + cap_info->cs1_row);
 | |
| 	else
 | |
| 		cap[1] = 0;
 | |
| 
 | |
| 	if (cs == 0)
 | |
| 		return cap[0];
 | |
| 	else if (cs == 1)
 | |
| 		return cap[1];
 | |
| 	else
 | |
| 		return (cap[0] + cap[1]);
 | |
| }
 | |
| 
 | |
| /* n: Unit bytes */
 | |
| void sdram_copy_to_reg(u32 *dest, const u32 *src, u32 n)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < n / sizeof(u32); i++) {
 | |
| 		writel(*src, dest);
 | |
| 		src++;
 | |
| 		dest++;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void sdram_org_config(struct sdram_cap_info *cap_info,
 | |
| 		      struct sdram_base_params *base,
 | |
| 		      u32 *p_os_reg2, u32 *p_os_reg3, u32 channel)
 | |
| {
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_DDRTYPE(base->dramtype);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_NUM_CH(base->num_channels);
 | |
| 
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_ROW_3_4(cap_info->row_3_4, channel);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_CHINFO(channel);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_RANK(cap_info->rank, channel);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_COL(cap_info->col, channel);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_BK(cap_info->bk, channel);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_BW(cap_info->bw, channel);
 | |
| 	*p_os_reg2 |= SYS_REG_ENC_DBW(cap_info->dbw, channel);
 | |
| 
 | |
| 	SYS_REG_ENC_CS0_ROW(cap_info->cs0_row, *p_os_reg2, *p_os_reg3, channel);
 | |
| 	if (cap_info->cs1_row)
 | |
| 		SYS_REG_ENC_CS1_ROW(cap_info->cs1_row, *p_os_reg2,
 | |
| 				    *p_os_reg3, channel);
 | |
| 	*p_os_reg3 |= SYS_REG_ENC_CS1_COL(cap_info->col, channel);
 | |
| 	*p_os_reg3 |= SYS_REG_ENC_VERSION(DDR_SYS_REG_VERSION);
 | |
| }
 | |
| 
 | |
| int sdram_detect_bw(struct sdram_cap_info *cap_info)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_cs(struct sdram_cap_info *cap_info)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_col(struct sdram_cap_info *cap_info,
 | |
| 		     u32 coltmp)
 | |
| {
 | |
| 	void __iomem *test_addr;
 | |
| 	u32 col;
 | |
| 	u32 bw = cap_info->bw;
 | |
| 
 | |
| 	for (col = coltmp; col >= 9; col -= 1) {
 | |
| 		writel(0, CONFIG_SYS_SDRAM_BASE);
 | |
| 		test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE +
 | |
| 				(1ul << (col + bw - 1ul)));
 | |
| 		writel(PATTERN, test_addr);
 | |
| 		if ((readl(test_addr) == PATTERN) &&
 | |
| 		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
 | |
| 			break;
 | |
| 	}
 | |
| 	if (col == 8) {
 | |
| 		printascii("col error\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	cap_info->col = col;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_bank(struct sdram_cap_info *cap_info,
 | |
| 		      u32 coltmp, u32 bktmp)
 | |
| {
 | |
| 	void __iomem *test_addr;
 | |
| 	u32 bk;
 | |
| 	u32 bw = cap_info->bw;
 | |
| 
 | |
| 	test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE +
 | |
| 			(1ul << (coltmp + bktmp + bw - 1ul)));
 | |
| 	writel(0, CONFIG_SYS_SDRAM_BASE);
 | |
| 	writel(PATTERN, test_addr);
 | |
| 	if ((readl(test_addr) == PATTERN) &&
 | |
| 	    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
 | |
| 		bk = 3;
 | |
| 	else
 | |
| 		bk = 2;
 | |
| 
 | |
| 	cap_info->bk = bk;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* detect bg for ddr4 */
 | |
| int sdram_detect_bg(struct sdram_cap_info *cap_info,
 | |
| 		    u32 coltmp)
 | |
| {
 | |
| 	void __iomem *test_addr;
 | |
| 	u32 dbw;
 | |
| 	u32 bw = cap_info->bw;
 | |
| 
 | |
| 	test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE +
 | |
| 			(1ul << (coltmp + bw + 1ul)));
 | |
| 	writel(0, CONFIG_SYS_SDRAM_BASE);
 | |
| 	writel(PATTERN, test_addr);
 | |
| 	if ((readl(test_addr) == PATTERN) &&
 | |
| 	    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
 | |
| 		dbw = 0;
 | |
| 	else
 | |
| 		dbw = 1;
 | |
| 
 | |
| 	cap_info->dbw = dbw;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* detect dbw for ddr3,lpddr2,lpddr3,lpddr4 */
 | |
| int sdram_detect_dbw(struct sdram_cap_info *cap_info, u32 dram_type)
 | |
| {
 | |
| 	u32 row, col, bk, bw, cs_cap, cs;
 | |
| 	u32 die_bw_0 = 0, die_bw_1 = 0;
 | |
| 
 | |
| 	if (dram_type == DDR3 || dram_type == LPDDR4) {
 | |
| 		cap_info->dbw = 1;
 | |
| 	} else if (dram_type == LPDDR3 || dram_type == LPDDR2) {
 | |
| 		row = cap_info->cs0_row;
 | |
| 		col = cap_info->col;
 | |
| 		bk = cap_info->bk;
 | |
| 		cs = cap_info->rank;
 | |
| 		bw = cap_info->bw;
 | |
| 		cs_cap = (1 << (row + col + bk + bw - 20));
 | |
| 		if (bw == 2) {
 | |
| 			if (cs_cap <= 0x2000000) /* 256Mb */
 | |
| 				die_bw_0 = (col < 9) ? 2 : 1;
 | |
| 			else if (cs_cap <= 0x10000000) /* 2Gb */
 | |
| 				die_bw_0 = (col < 10) ? 2 : 1;
 | |
| 			else if (cs_cap <= 0x40000000) /* 8Gb */
 | |
| 				die_bw_0 = (col < 11) ? 2 : 1;
 | |
| 			else
 | |
| 				die_bw_0 = (col < 12) ? 2 : 1;
 | |
| 			if (cs > 1) {
 | |
| 				row = cap_info->cs1_row;
 | |
| 				cs_cap = (1 << (row + col + bk + bw - 20));
 | |
| 				if (cs_cap <= 0x2000000) /* 256Mb */
 | |
| 					die_bw_0 = (col < 9) ? 2 : 1;
 | |
| 				else if (cs_cap <= 0x10000000) /* 2Gb */
 | |
| 					die_bw_0 = (col < 10) ? 2 : 1;
 | |
| 				else if (cs_cap <= 0x40000000) /* 8Gb */
 | |
| 					die_bw_0 = (col < 11) ? 2 : 1;
 | |
| 				else
 | |
| 					die_bw_0 = (col < 12) ? 2 : 1;
 | |
| 			}
 | |
| 		} else {
 | |
| 			die_bw_1 = 1;
 | |
| 			die_bw_0 = 1;
 | |
| 		}
 | |
| 		cap_info->dbw = (die_bw_0 > die_bw_1) ? die_bw_0 : die_bw_1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_row(struct sdram_cap_info *cap_info,
 | |
| 		     u32 coltmp, u32 bktmp, u32 rowtmp)
 | |
| {
 | |
| 	u32 row;
 | |
| 	u32 bw = cap_info->bw;
 | |
| 	void __iomem *test_addr;
 | |
| 
 | |
| 	for (row = rowtmp; row > 12; row--) {
 | |
| 		writel(0, CONFIG_SYS_SDRAM_BASE);
 | |
| 		test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE +
 | |
| 				(1ul << (row + bktmp + coltmp + bw - 1ul)));
 | |
| 		writel(PATTERN, test_addr);
 | |
| 		if ((readl(test_addr) == PATTERN) &&
 | |
| 		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
 | |
| 			break;
 | |
| 	}
 | |
| 	if (row == 12) {
 | |
| 		printascii("row error");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	cap_info->cs0_row = row;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_row_3_4(struct sdram_cap_info *cap_info,
 | |
| 			 u32 coltmp, u32 bktmp)
 | |
| {
 | |
| 	u32 row_3_4;
 | |
| 	u32 bw = cap_info->bw;
 | |
| 	u32 row = cap_info->cs0_row;
 | |
| 	void __iomem *test_addr, *test_addr1;
 | |
| 
 | |
| 	test_addr = CONFIG_SYS_SDRAM_BASE;
 | |
| 	test_addr1 = (void __iomem *)(CONFIG_SYS_SDRAM_BASE +
 | |
| 			(0x3ul << (row + bktmp + coltmp + bw - 1ul - 1ul)));
 | |
| 
 | |
| 	writel(0, test_addr);
 | |
| 	writel(PATTERN, test_addr1);
 | |
| 	if ((readl(test_addr) == 0) && (readl(test_addr1) == PATTERN))
 | |
| 		row_3_4 = 0;
 | |
| 	else
 | |
| 		row_3_4 = 1;
 | |
| 
 | |
| 	cap_info->row_3_4 = row_3_4;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_high_row(struct sdram_cap_info *cap_info)
 | |
| {
 | |
| 	cap_info->cs0_high16bit_row = cap_info->cs0_row;
 | |
| 	cap_info->cs1_high16bit_row = cap_info->cs1_row;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int sdram_detect_cs1_row(struct sdram_cap_info *cap_info, u32 dram_type)
 | |
| {
 | |
| 	void __iomem *test_addr;
 | |
| 	u32 row = 0, bktmp, coltmp, bw;
 | |
| 	ulong cs0_cap;
 | |
| 	u32 byte_mask;
 | |
| 
 | |
| 	if (cap_info->rank == 2) {
 | |
| 		cs0_cap = sdram_get_cs_cap(cap_info, 0, dram_type);
 | |
| 
 | |
| 		if (dram_type == DDR4) {
 | |
| 			if (cap_info->dbw == 0)
 | |
| 				bktmp = cap_info->bk + 2;
 | |
| 			else
 | |
| 				bktmp = cap_info->bk + 1;
 | |
| 		} else {
 | |
| 			bktmp = cap_info->bk;
 | |
| 		}
 | |
| 		bw = cap_info->bw;
 | |
| 		coltmp = cap_info->col;
 | |
| 
 | |
| 		/*
 | |
| 		 * because px30 support axi split,min bandwidth
 | |
| 		 * is 8bit. if cs0 is 32bit, cs1 may 32bit or 16bit
 | |
| 		 * so we check low 16bit data when detect cs1 row.
 | |
| 		 * if cs0 is 16bit/8bit, we check low 8bit data.
 | |
| 		 */
 | |
| 		if (bw == 2)
 | |
| 			byte_mask = 0xFFFF;
 | |
| 		else
 | |
| 			byte_mask = 0xFF;
 | |
| 
 | |
| 		/* detect cs1 row */
 | |
| 		for (row = cap_info->cs0_row; row > 12; row--) {
 | |
| 			test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE +
 | |
| 				    cs0_cap +
 | |
| 				    (1ul << (row + bktmp + coltmp + bw - 1ul)));
 | |
| 			writel(0, CONFIG_SYS_SDRAM_BASE + cs0_cap);
 | |
| 			writel(PATTERN, test_addr);
 | |
| 
 | |
| 			if (((readl(test_addr) & byte_mask) ==
 | |
| 			     (PATTERN & byte_mask)) &&
 | |
| 			    ((readl(CONFIG_SYS_SDRAM_BASE + cs0_cap) &
 | |
| 			      byte_mask) == 0)) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cap_info->cs1_row = row;
 | |
| 
 | |
| 	return 0;
 | |
| }
 |