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, ");
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 (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;
}
原作者:火柴棍mcu