RockChip U-Boot SPL/TPL 移植

U-Boot TPL是一段非常简短的代码,目的是初始化RAM,最近到手了一个自己的PX30板子,没有任何资料,于是就打算上主线代码一步一步来,一启动,喜闻乐见:

U-Boot TPL board init
sdram_init failed

而TPL的C入口如下(至于怎么进入board_init_f没有研究的意义,网上一大堆.):


void board_init_f(ulong dummy)
{
	int ret;

#ifdef CONFIG_DEBUG_UART
	debug_uart_init();
	/*
	 * Debug UART can be used from here if required:
	 *
	 * debug_uart_init();
	 * printch('a');
	 * printhex8(0x1234);
	 * printascii("string");
	 */
	printascii("U-Boot TPL board init\n");
#endif

	secure_timer_init();
	ret = sdram_init();
	if (ret)
		printascii("sdram_init failed\n");

	/* return to maskrom */
	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
}

由因为Secure Timer是Trusted Zone的内容,所以他肯定也不会出错,除非处理器本身是问题的,也就剩下sdram_init函数了.而PX30的DDR引脚是固定的,也就是没有配置的可能性,所以问题一定在其他地方,根据观察板上焊的是IMG512M32V1D2CEL-107,由于这个板子之前是用在其他产品,说明一定支持这个内存.查得参数是LPDDR3 933MHz @ 16Gbits,因此我也打开了CONFIG_RAM_PX30_LPDDR3这个宏定义.

/* return: 0 = success, other = fail */
int sdram_init(void)
{
	struct px30_sdram_params *sdram_params;
	int ret = 0;

	dram_info.phy = (void *)DDR_PHY_BASE_ADDR;
	dram_info.pctl = (void *)DDRC_BASE_ADDR;
	dram_info.grf = (void *)GRF_BASE_ADDR;
	dram_info.cru = (void *)CRU_BASE_ADDR;
	dram_info.msch = (void *)SERVER_MSCH0_BASE_ADDR;
	dram_info.ddr_grf = (void *)DDR_GRF_BASE_ADDR;
	dram_info.pmugrf = (void *)PMUGRF_BASE_ADDR;

	sdram_params = get_default_sdram_config();
	ret = sdram_init_detect(&dram_info, sdram_params);

	if (ret)
		goto error;

	sdram_print_ddr_info(&sdram_params->ch.cap_info, &sdram_params->base);

	printascii("out\n");
	return ret;
error:
	return (-1);
}

这是我网上随便很辛苦找的其他PX30的参考原理图.

可见引脚没什么选择的余地,这样更好,说明不会涉及改太多配置.

/* return: 0 = success, other = fail */
int sdram_init(void)
{
	struct px30_sdram_params *sdram_params;
	int ret = 0;

	dram_info.phy = (void *)DDR_PHY_BASE_ADDR;
	dram_info.pctl = (void *)DDRC_BASE_ADDR;
	dram_info.grf = (void *)GRF_BASE_ADDR;
	dram_info.cru = (void *)CRU_BASE_ADDR;
	dram_info.msch = (void *)SERVER_MSCH0_BASE_ADDR;
	dram_info.ddr_grf = (void *)DDR_GRF_BASE_ADDR;
	dram_info.pmugrf = (void *)PMUGRF_BASE_ADDR;

	sdram_params = get_default_sdram_config();
	ret = sdram_init_detect(&dram_info, sdram_params);

	if (ret)
		goto error;

	sdram_print_ddr_info(&sdram_params->ch.cap_info, &sdram_params->base);

	printascii("out\n");
	return ret;
error:
	return (-1);
}

出问题的是sdram_init_detect,因为没打印out字样,所以跟进分析.

/*
 * pre_init: 0: pre init for dram cap detect
 * 1: detect correct cap(except cs1 row)info, than reinit
 * 2: after reinit, we detect cs1_row, if cs1_row not equal
 *    to cs0_row and cs is in middle on ddrconf map, we need
 *    to reinit dram, than set the correct ddrconf.
 */
static int sdram_init_(struct dram_info *dram,
		       struct px30_sdram_params *sdram_params, u32 pre_init)
{
	struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info;
	void __iomem *pctl_base = dram->pctl;

	rkclk_ddr_reset(dram, 1, 1, 1, 1); //复位DDR控制器之类,这些部分一定不会出问题.
	udelay(10);
	/*
	 * dereset ddr phy psrstn to config pll,
	 * if using phy pll psrstn must be dereset
	 * before config pll
	 */
	rkclk_ddr_reset(dram, 1, 1, 1, 0);
	rkclk_configure_ddr(dram, sdram_params);

	/* release phy srst to provide clk to ctrl */
	rkclk_ddr_reset(dram, 1, 1, 0, 0);
	udelay(10);
	phy_soft_reset(dram->phy);
	/* release ctrl presetn, and config ctl registers */
	rkclk_ddr_reset(dram, 1, 0, 0, 0);
	pctl_cfg(dram->pctl, &sdram_params->pctl_regs, SR_IDLE, PD_IDLE);
	cap_info->ddrconfig = calculate_ddrconfig(sdram_params);
	set_ctl_address_map(dram, sdram_params);
	phy_cfg(dram->phy, &sdram_params->phy_regs, sdram_params->skew,
		&sdram_params->base, cap_info->bw);

	/* enable dfi_init_start to init phy after ctl srstn deassert */
	setbits_le32(pctl_base + DDR_PCTL2_DFIMISC, (1 << 5) | (1 << 4));

	rkclk_ddr_reset(dram, 0, 0, 0, 0);
	/* wait for dfi_init_done and dram init complete */
	while ((readl(pctl_base + DDR_PCTL2_STAT) & 0x7) == 0)
		continue;

	if (sdram_params->base.dramtype == LPDDR3) //打印一下这个判断.
		pctl_write_mr(dram->pctl, 3, 11, 3, LPDDR3);

	/* do ddr gate training */
redo_cs0_training:
	if (data_training(dram, 0, sdram_params->base.dramtype) != 0) {
		if (pre_init != 0)
			printascii("DTT cs0 error\n");
		return -1;
	}
	if (check_rd_gate(dram)) {
		printascii("re training cs0");
		goto redo_cs0_training;
	}

	if (sdram_params->base.dramtype == LPDDR3) {
		if ((read_mr(dram, 1, 8) & 0x3) != 0x3)
			return -1;
	} else if (sdram_params->base.dramtype == LPDDR2) {
		if ((read_mr(dram, 1, 8) & 0x3) != 0x0)
			return -1;
	}
	/* for px30: when 2cs, both 2 cs should be training */
	if (pre_init != 0 && cap_info->rank == 2) {
redo_cs1_training:
		if (data_training(dram, 1, sdram_params->base.dramtype) != 0) {
			printascii("DTT cs1 error\n");
			return -1;
		}
		if (check_rd_gate(dram)) {
			printascii("re training cs1");
			goto redo_cs1_training;
		}
	}

	if (sdram_params->base.dramtype == DDR4)
		pctl_write_vrefdq(dram->pctl, 0x3, 5670,
				  sdram_params->base.dramtype);

	dram_all_config(dram, sdram_params);
	enable_low_power(dram, sdram_params);

	return 0;
}

/* return: 0 = success, other = fail */
static int sdram_init_detect(struct dram_info *dram,
			     struct px30_sdram_params *sdram_params)
{
	struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info;
	u32 ret;
	u32 sys_reg = 0;
	u32 sys_reg3 = 0;

	if (sdram_init_(dram, sdram_params, 0) != 0)
		return -1;

	if (dram_detect_cap(dram, sdram_params, 0) != 0)
		return -1;

	/* modify bw, cs related timing */
	pctl_remodify_sdram_params(&sdram_params->pctl_regs, cap_info,
				   sdram_params->base.dramtype);
	/* reinit sdram by real dram cap */
	ret = sdram_init_(dram, sdram_params, 1);
	if (ret != 0)
		goto out;

	/* redetect cs1 row */
	sdram_detect_cs1_row(cap_info, sdram_params->base.dramtype);
	if (cap_info->cs1_row) {
		sys_reg = readl(&dram->pmugrf->os_reg[2]);
		sys_reg3 = readl(&dram->pmugrf->os_reg[3]);
		SYS_REG_ENC_CS1_ROW(cap_info->cs1_row,
				    sys_reg, sys_reg3, 0);
		writel(sys_reg, &dram->pmugrf->os_reg[2]);
		writel(sys_reg3, &dram->pmugrf->os_reg[3]);
	}

	ret = sdram_detect_high_row(cap_info);

out:
	return ret;
}

最后发现dramtype判断错误,最后发现CONFIG_RAM_PX30_LPDDR3同时开启了好几个,最后关闭其他RAM判断,只留其中一个就好.

if (sdram_params->base.dramtype == LPDDR3)

最后引导就过了这部分.

U-Boot TPL board init
LPDDR3, 333MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
out

U-Boot SPL 2021.07-rc4-g97c8cb5-dirty (Jun 21 2021 - 21:08:49 +0800)
Trying to boot from MMC1

终于可以进入SPL了,然后卡在这里.

/**
 * boot_from_devices() - Try loading a booting U-Boot from a list of devices
 *
 * @spl_image: Place to put the image details if successful
 * @spl_boot_list: List of boot devices to try
 * @count: Number of elements in spl_boot_list
 * @return 0 if OK, -ve on error
 */
static int boot_from_devices(struct spl_image_info *spl_image,
			     u32 spl_boot_list[], int count)
{
	int i;

	for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
		struct spl_image_loader *loader;

		loader = spl_ll_find_loader(spl_boot_list[i]);
#if defined(CONFIG_SPL_SERIAL_SUPPORT) \
    && defined(CONFIG_SPL_LIBCOMMON_SUPPORT)    \
    && !defined(CONFIG_SILENT_CONSOLE)
		if (loader)
			printf("Trying to boot from %s\n", loader->name); //遇到这里之后就直接GG了.
		else
			puts(SPL_TPL_PROMPT "Unsupported Boot Device!\n");
#endif
		if (loader && !spl_load_image(spl_image, loader)) {
			spl_image->boot_device = spl_boot_list[i];
			return 0;
		}
	}

	return -ENODEV;
}

因为上面判断到Loader非空,所以一定可以加载spl_load_image,先不论MMC1是不是正确的设备,至少发现了MMC1,然后会调用loader的load_image方法.

static int spl_load_image(struct spl_image_info *spl_image,
			  struct spl_image_loader *loader)
{
	int ret;
	struct spl_boot_device bootdev;

	bootdev.boot_device = loader->boot_device;
	bootdev.boot_device_name = NULL;

	ret = loader->load_image(spl_image, &bootdev);
#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK
	if (!ret && spl_image->dcrc_length) {
		/* check data crc */
		ulong dcrc = crc32_wd(0, (unsigned char *)spl_image->dcrc_data,
				      spl_image->dcrc_length, CHUNKSZ_CRC32);
		if (dcrc != spl_image->dcrc) {
			puts("SPL: Image data CRC check failed!\n");
			ret = -EINVAL;
		}
	}
#endif
	return ret;
}

所以这里要往上分析,发现实际从FDT取数据,然后检查FDT正确,检查返回为0,正确,最后进到bl31_entry然后卡死.

static void bl31_entry(uintptr_t bl31_entry, uintptr_t bl32_entry,
		       uintptr_t bl33_entry, uintptr_t fdt_addr)
{
	atf_entry_t  atf_entry = (atf_entry_t)bl31_entry;
	void *bl31_params;

	if (CONFIG_IS_ENABLED(ATF_LOAD_IMAGE_V2))
		bl31_params = bl2_plat_get_bl31_params_v2(bl32_entry,
							  bl33_entry,
							  fdt_addr);
	else
		bl31_params = bl2_plat_get_bl31_params(bl32_entry, bl33_entry,
						       fdt_addr);

	raw_write_daif(SPSR_EXCEPTION_MASK);
	dcache_disable();

	atf_entry(bl31_params, (void *)fdt_addr);
}

后检查ATF_LOAD_IMAGE_V2没打开,由于我下载的是主线ATF,所以这个应该打开.(毕竟不是旧版了)

最后顺利启动了~

U-Boot TPL board init
LPDDR3, 333MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
out

U-Boot SPL 2021.07-rc4-g386a08b-dirty (Jun 22 2021 - 00:21:54 +0800)
Trying to boot from MMC1
mmc_load_image_raw_sector: mmc block read error
NOTICE:  BL31: v2.5(release):2a00877
NOTICE:  BL31: Built : 13:26:52, Jun 21 2021


U-Boot 2021.07-rc4-g386a08b-dirty (Jun 22 2021 - 00:22:02 +0800)

Model: Rockchip PX30 EVB
DRAM:  2 GiB
PMIC:  RK8090 (on=0x10, off=0x00)
MMC:   dwmmc@ff380000: 2, dwmmc@ff390000: 0
Loading Environment from MMC... *** Warning - bad CRC, using default environment

In:    serial@ff160000
Out:   serial@ff160000
Err:   serial@ff160000
Model: Rockchip PX30 EVB
Net:   Could not get PHY for ethernet@ff360000: addr -1
No ethernet found.

Hit any key to stop autoboot:  0
switch to partitions #0, OK
mmc0(part 0) is current device
** Unrecognized filesystem type **
MMC Device 1 not found
no mmc device at slot 1
starting USB...
Bus usb@ff340000: USB EHCI 1.00
scanning bus usb@ff340000 for devices... 2 USB Device(s) found
       scanning usb for storage devices... 0 Storage Device(s) found

Device 0: unknown device
No ethernet found.
missing environment variable: pxeuuid
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm-px30-evb_px30
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm-px30
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default
No ethernet found.
Config file not found
No ethernet found.
=>

后续再看如何移植U-Boot本体.

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注