1.硬件复位后重启日志

Bootdev(atags): mmc 0
MMC0: HS200, 200Mhz
PartType: EFI
boot mode: None
FIT: no signed, no conf required
DTB: rk-kernel.dtb
HASH(c): OK
I2c0 speed: 100000Hz
vsel-gpios- not found! Error: -2
vdd_cpu init 900000 uV
PMIC:  RK8170 (on=0x10, off=0x00)
vdd_logic init 900000 uV
vdd_gpu init 900000 uV
4200000 uV is too high from LDO_REG5
io-domain: OK
Warn: can't find connect driver
Could not find baseparameter partition
Model: Rockchip RK3566 RK817 BDTTS402 LP4X Board
rk8xx_read: read reg 0x4d failed, ret=-121
rk8xx_write: write reg 0x4d failed, ret=-121
__virq_enable: Clear status register 0x4d failed, ret=-121
此时液晶屏无logo显示。
停留1分钟
继续输出如下日志:
Rockchip UBOOT DRM driver version: v1.0.1
VOP have 1 active VP
vp0 have layer nr:6[0 2 4 1 3 5 ], primary plane: 4
vp1 have layer nr:0[], primary plane: 0
vp2 have layer nr:0[], primary plane: 0
Using display timing dts
dsi@fe060000:  detailed mode clock 29000 kHz, flags[8000000a]
    H: 0480 0508 0512 0540
    V: 0854 0882 0886 0914
bus_format: 100e
VOP update mode to: 480x854p0, type: MIPI0 for VP0
VOP VP0 enable Smart0[480x854->480x854@0x0] fmt[1] addr[0x7df00036]
final DSI-Link bandwidth: 384 Mbps x 2
CLK: (sync kernel. arm: enter 816000 KHz, init 816000 KHz, kernel 0N/A)
  apll 1416000 KHz
  dpll 460000 KHz
  gpll 1188000 KHz
  cpll 1000000 KHz
  npll 1200000 KHz
  vpll 24000 KHz
  hpll 29000 KHz
  ppll 200000 KHz
  armclk 1416000 KHz
  aclk_bus 150000 KHz
  pclk_bus 100000 KHz
  aclk_top_high 500000 KHz
  aclk_top_low 400000 KHz
  hclk_top 150000 KHz
  pclk_top 100000 KHz
  aclk_perimid 300000 KHz
  hclk_perimid 150000 KHz
  pclk_pmu 100000 KHz
Net:   eth1: ethernet@fe010000
Hit key to stop autoboot('CTRL+C'):  0 
## Booting FIT Image at 0x7a317f80 with size 0x014dfc00
Fdt Ramdisk skip relocation
## Loading kernel from FIT Image at 7a317f80 ...
   Using 'conf' configuration
optee api revision: 2.0
TEEC: Waring: Could not find security partition
此时液晶屏可以显示logo。

2.对比android镜像

烧写厂家提供的android镜像则不会存在此问题。把linux镜像中的uboot.img换为android下的uboot.img也不会存在此问题,不过会出现另一个问题,就是内核启动后液晶就无显示了,如果没有这个问题其实可以直接用android下的u-boot源码替换linux下的u-boot源码。也尝试对比两个u-boot源码的差异,发现很多文件都不同。

3.找出关键的问题

通过在..\x3566_linux_v1.2.0\u-boot\arch\arm\mach-rockchip\board.c文件的board_late_init()函数中加入一些日志,发现是执行setup_download_mode()函数返回的错误,屏蔽此函数后则硬件复位后系统能正常启动。不过android源码中也调用了此函数。屏蔽此函数后启动时无法通过按下recovery按键进入loader模式。

通过搜索setup_download_mode()函数的实现在..\x3566_linux_v1.2.0\u-boot\arch\arm\mach-rockchip\boot_rkimg.c文件中:

void setup_download_mode(void)
{
	int vbus = 1; /* Assumed 1 in case of no rockusb */

	boot_devtype_init();

	/*
	 * rockchip_dnl_key_pressed():
	 *
	 * (1) volume-up key (default)
	 * (2) menu key (If no rockusb)
	 *
	 * It's possible that USB is disabled due to developer needs
	 * a critial size of u-boot.bin.
	 *
	 * Disabling USB makes vbus can't be detected any more, so that
	 * we add menu key. The events trigger are changed:
	 *
	 * - rockusb mode(actually fallback to bootrom mode):
	 *	"volume-up pressed + vbus=1" replaced with "menu pressed"
	 * - recovery mode:
	 *	"volume-up pressed + vbus=0" replaced with "volume-up pressed"
	 *
	 * At the most time, USB is enabled and this feature is not applied.
	 */
	if (rockchip_dnl_key_pressed() || is_hotkey(HK_ROCKUSB_DNL)) {
		printf("download %skey pressed... ",
		       is_hotkey(HK_ROCKUSB_DNL) ? "hot" : "");
#ifdef CONFIG_CMD_ROCKUSB
		vbus = rockchip_u2phy_vbus_detect();
#endif
		if (vbus > 0) {
			printf("%sentering download mode...\n",
			       IS_ENABLED(CONFIG_CMD_ROCKUSB) ?
			       "" : "no rockusb, ");

			/* try rockusb download and brom download */
			run_command("download", 0);
		} else {
			printf("entering recovery mode!\n");
			env_set("reboot_mode", "recovery-key");
		}
	} else if (is_hotkey(HK_FASTBOOT)) {
		env_set("reboot_mode", "fastboot");
	}
}

进一步探索,发现是uboot卡死在rockchip_dnl_key_pressed()函数的return key_is_pressed(key_read(KEY_VOLUMEUP));语句中:

__weak int rockchip_dnl_key_pressed(void)
{
#if defined(CONFIG_DM_KEY)
#ifdef CONFIG_CMD_ROCKUSB
	printf("key_is_pressed VOLUMEUP\n");
	return key_is_pressed(key_read(KEY_VOLUMEUP));
#else
	printf("key_is_pressed MENU\n");
	return key_is_pressed(key_read(KEY_MENU));
#endif

#elif defined(CONFIG_ADC)
	const void *blob = gd->fdt_blob;
	int node, ret, channel = 1;
	u32 val, chns[2];

	node = fdt_node_offset_by_compatible(blob, 0, "adc-keys");
	
	printf("fdt_node_offset_by_compatible\n");
	if (node >= 0) {
		if (!fdtdec_get_int_array(blob, node, "io-channels", chns, 2))
			channel = chns[1];
	}

	ret = adc_channel_single_shot("saradc", channel, &val);
	if (ret) {
		printf("%s: Failed to read saradc, ret=%d\n", __func__, ret);
		return 0;
	}
	
	printf("adc_channel_single_shot\n");
	
	return ((val >= KEY_DOWN_MIN_VAL) && (val <= KEY_DOWN_MAX_VAL));
#endif

	return 0;
}

继续深入到..\x3566_linux_v1.2.0\u-boot\drivers\input\key-uclass.c文件,此文件实现了int key_read(int code)函数:

int key_read(int code)
{
	struct dm_key_uclass_platdata *uc_key;
	struct udevice *dev;
	struct uclass *uc;
	bool allow_pre_reloc = false;
	int ret, event = KEY_NOT_EXIST;

	ret = uclass_get(UCLASS_KEY, &uc);
	if (ret)
		return ret;

try_again:
	for (uclass_first_device(UCLASS_KEY, &dev);
	     dev;
	     uclass_next_device(&dev)) {
		uc_key = dev_get_uclass_platdata(dev);

		if (!allow_pre_reloc && uc_key->pre_reloc)
			continue;

		if (uc_key->code != code)
			continue;

		event = key_core_read(uc_key);
		if (key_is_pressed(event))
			return event;
	}

	/* If not find valid key node from kernel, try from u-boot */
	if (event == KEY_NOT_EXIST && !allow_pre_reloc) {
		allow_pre_reloc = true;
		goto try_again;
	}

	return event;
}

dev_get_uclass_platdata()函数的实现在..\x3566_linux_v1.2.0\u-boot\drivers\core\device.c文件中。

4.根据日志猜测问题在读取PMIC INT寄出器时出错

rk8xx_read: read reg 0x4d failed, ret=-121
rk8xx_write: write reg 0x4d failed, ret=-121
__virq_enable: Clear status register 0x4d failed, ret=-121

根据以上日志,猜测应该是PMIC产生了中断事件,处理器读取PMIC INT寄存器时出错,也没有清除掉PMIC的中断,导致中断一直存在,程序卡死。

从日志看出当前是在读取0x04寄存器,而RK817的INT MASK寄存器为0xf9,RK805、RK808、RK818的INT MASK寄存器为0x4d。

怀疑是某个过程PMIC的ID传错了,通过加日志排查,int rk8xx_irq_chip_init(struct udevice *dev)函数中PMIC的ID为RK808,所以注册的中断相关的寄存器也是RK808的,导致读取错误。

修改int rk8xx_probe(struct udevice *dev)函数,给priv->variant赋值:

static int rk8xx_probe(struct udevice *dev)
{
...
--  show_variant = ((msb << 8) | lsb) & RK8XX_ID_MSK;
++	priv->variant = ((msb << 8) | lsb) & RK8XX_ID_MSK;
++	show_variant = priv->variant;
...
}

修改之后运行还是报错,只是寄存器变了: 

rk8xx_read: read reg 0xf9 failed, ret=-121
rk8xx_write: write reg 0xf9 failed, ret=-121
__virq_enable: Clear status register 0xf9 failed, ret=-121

从此现象可以得出错误的主要原因不是寄存器配置错误,而是其它未知的原因。

5.解决方案

目前将rk8xx_pwrkey.c文件中的中断使能函数屏蔽,即可避免此问题:

static int rk8xx_pwrkey_probe(struct udevice *dev)
{
	printf("rk8xx_pwrkey_probe\n");
	struct rk8xx_priv *rk8xx = dev_get_priv(dev->parent);
	struct dm_key_uclass_platdata *key = dev_get_uclass_platdata(dev);
	int fall_irq, rise_irq;

	if (!rk8xx->irq_chip) {
		printf("Failed to get parent irq chip\n");
		return -ENOENT;
	}

	fall_irq = virq_to_irq(rk8xx->irq_chip, RK8XX_IRQ_PWRON_FALL);
	if (fall_irq < 0) {
		printf("Failed to register pwron fall irq, ret=%d\n", fall_irq);
		return fall_irq;
	}

	rise_irq = virq_to_irq(rk8xx->irq_chip, RK8XX_IRQ_PWRON_RISE);
	if (rise_irq < 0) {
		printf("Failed to register pwron rise irq, ret=%d\n", rise_irq);
		return rise_irq;
	}

	key->name = "rk8xx_pwr";
	key->type = GPIO_KEY;
	key->code = KEY_POWER;
	key->skip_irq_init = 1;

	irq_install_handler(fall_irq, rk8xx_pwron_fall_handler, dev);
	irq_install_handler(rise_irq, rk8xx_pwron_rise_handler, dev);
//	irq_handler_enable(fall_irq);
//	irq_handler_enable(rise_irq);

	return 0;
}