• RK3566 linux系统硬件复位后无法重启


    1.硬件复位后重启日志

    1. Bootdev(atags): mmc 0
    2. MMC0: HS200, 200Mhz
    3. PartType: EFI
    4. boot mode: None
    5. FIT: no signed, no conf required
    6. DTB: rk-kernel.dtb
    7. HASH(c): OK
    8. I2c0 speed: 100000Hz
    9. vsel-gpios- not found! Error: -2
    10. vdd_cpu init 900000 uV
    11. PMIC: RK8170 (on=0x10, off=0x00)
    12. vdd_logic init 900000 uV
    13. vdd_gpu init 900000 uV
    14. 4200000 uV is too high from LDO_REG5
    15. io-domain: OK
    16. Warn: can't find connect driver
    17. Could not find baseparameter partition
    18. Model: Rockchip RK3566 RK817 BDTTS402 LP4X Board
    19. rk8xx_read: read reg 0x4d failed, ret=-121
    20. rk8xx_write: write reg 0x4d failed, ret=-121
    21. __virq_enable: Clear status register 0x4d failed, ret=-121
    22. 此时液晶屏无logo显示。
    23. 停留1分钟
    24. 继续输出如下日志:
    25. Rockchip UBOOT DRM driver version: v1.0.1
    26. VOP have 1 active VP
    27. vp0 have layer nr:6[0 2 4 1 3 5 ], primary plane: 4
    28. vp1 have layer nr:0[], primary plane: 0
    29. vp2 have layer nr:0[], primary plane: 0
    30. Using display timing dts
    31. dsi@fe060000: detailed mode clock 29000 kHz, flags[8000000a]
    32. H: 0480 0508 0512 0540
    33. V: 0854 0882 0886 0914
    34. bus_format: 100e
    35. VOP update mode to: 480x854p0, type: MIPI0 for VP0
    36. VOP VP0 enable Smart0[480x854->480x854@0x0] fmt[1] addr[0x7df00036]
    37. final DSI-Link bandwidth: 384 Mbps x 2
    38. CLK: (sync kernel. arm: enter 816000 KHz, init 816000 KHz, kernel 0N/A)
    39. apll 1416000 KHz
    40. dpll 460000 KHz
    41. gpll 1188000 KHz
    42. cpll 1000000 KHz
    43. npll 1200000 KHz
    44. vpll 24000 KHz
    45. hpll 29000 KHz
    46. ppll 200000 KHz
    47. armclk 1416000 KHz
    48. aclk_bus 150000 KHz
    49. pclk_bus 100000 KHz
    50. aclk_top_high 500000 KHz
    51. aclk_top_low 400000 KHz
    52. hclk_top 150000 KHz
    53. pclk_top 100000 KHz
    54. aclk_perimid 300000 KHz
    55. hclk_perimid 150000 KHz
    56. pclk_pmu 100000 KHz
    57. Net: eth1: ethernet@fe010000
    58. Hit key to stop autoboot('CTRL+C'): 0
    59. ## Booting FIT Image at 0x7a317f80 with size 0x014dfc00
    60. Fdt Ramdisk skip relocation
    61. ## Loading kernel from FIT Image at 7a317f80 ...
    62. Using 'conf' configuration
    63. optee api revision: 2.0
    64. TEEC: Waring: Could not find security partition
    65. 此时液晶屏可以显示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文件中:

    1. void setup_download_mode(void)
    2. {
    3. int vbus = 1; /* Assumed 1 in case of no rockusb */
    4. boot_devtype_init();
    5. /*
    6. * rockchip_dnl_key_pressed():
    7. *
    8. * (1) volume-up key (default)
    9. * (2) menu key (If no rockusb)
    10. *
    11. * It's possible that USB is disabled due to developer needs
    12. * a critial size of u-boot.bin.
    13. *
    14. * Disabling USB makes vbus can't be detected any more, so that
    15. * we add menu key. The events trigger are changed:
    16. *
    17. * - rockusb mode(actually fallback to bootrom mode):
    18. * "volume-up pressed + vbus=1" replaced with "menu pressed"
    19. * - recovery mode:
    20. * "volume-up pressed + vbus=0" replaced with "volume-up pressed"
    21. *
    22. * At the most time, USB is enabled and this feature is not applied.
    23. */
    24. if (rockchip_dnl_key_pressed() || is_hotkey(HK_ROCKUSB_DNL)) {
    25. printf("download %skey pressed... ",
    26. is_hotkey(HK_ROCKUSB_DNL) ? "hot" : "");
    27. #ifdef CONFIG_CMD_ROCKUSB
    28. vbus = rockchip_u2phy_vbus_detect();
    29. #endif
    30. if (vbus > 0) {
    31. printf("%sentering download mode...\n",
    32. IS_ENABLED(CONFIG_CMD_ROCKUSB) ?
    33. "" : "no rockusb, ");
    34. /* try rockusb download and brom download */
    35. run_command("download", 0);
    36. } else {
    37. printf("entering recovery mode!\n");
    38. env_set("reboot_mode", "recovery-key");
    39. }
    40. } else if (is_hotkey(HK_FASTBOOT)) {
    41. env_set("reboot_mode", "fastboot");
    42. }
    43. }

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

    1. __weak int rockchip_dnl_key_pressed(void)
    2. {
    3. #if defined(CONFIG_DM_KEY)
    4. #ifdef CONFIG_CMD_ROCKUSB
    5. printf("key_is_pressed VOLUMEUP\n");
    6. return key_is_pressed(key_read(KEY_VOLUMEUP));
    7. #else
    8. printf("key_is_pressed MENU\n");
    9. return key_is_pressed(key_read(KEY_MENU));
    10. #endif
    11. #elif defined(CONFIG_ADC)
    12. const void *blob = gd->fdt_blob;
    13. int node, ret, channel = 1;
    14. u32 val, chns[2];
    15. node = fdt_node_offset_by_compatible(blob, 0, "adc-keys");
    16. printf("fdt_node_offset_by_compatible\n");
    17. if (node >= 0) {
    18. if (!fdtdec_get_int_array(blob, node, "io-channels", chns, 2))
    19. channel = chns[1];
    20. }
    21. ret = adc_channel_single_shot("saradc", channel, &val);
    22. if (ret) {
    23. printf("%s: Failed to read saradc, ret=%d\n", __func__, ret);
    24. return 0;
    25. }
    26. printf("adc_channel_single_shot\n");
    27. return ((val >= KEY_DOWN_MIN_VAL) && (val <= KEY_DOWN_MAX_VAL));
    28. #endif
    29. return 0;
    30. }

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

    1. int key_read(int code)
    2. {
    3. struct dm_key_uclass_platdata *uc_key;
    4. struct udevice *dev;
    5. struct uclass *uc;
    6. bool allow_pre_reloc = false;
    7. int ret, event = KEY_NOT_EXIST;
    8. ret = uclass_get(UCLASS_KEY, &uc);
    9. if (ret)
    10. return ret;
    11. try_again:
    12. for (uclass_first_device(UCLASS_KEY, &dev);
    13. dev;
    14. uclass_next_device(&dev)) {
    15. uc_key = dev_get_uclass_platdata(dev);
    16. if (!allow_pre_reloc && uc_key->pre_reloc)
    17. continue;
    18. if (uc_key->code != code)
    19. continue;
    20. event = key_core_read(uc_key);
    21. if (key_is_pressed(event))
    22. return event;
    23. }
    24. /* If not find valid key node from kernel, try from u-boot */
    25. if (event == KEY_NOT_EXIST && !allow_pre_reloc) {
    26. allow_pre_reloc = true;
    27. goto try_again;
    28. }
    29. return event;
    30. }

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

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

    1. rk8xx_read: read reg 0x4d failed, ret=-121
    2. rk8xx_write: write reg 0x4d failed, ret=-121
    3. __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赋值:

    1. static int rk8xx_probe(struct udevice *dev)
    2. {
    3. ...
    4. -- show_variant = ((msb << 8) | lsb) & RK8XX_ID_MSK;
    5. ++ priv->variant = ((msb << 8) | lsb) & RK8XX_ID_MSK;
    6. ++ show_variant = priv->variant;
    7. ...
    8. }

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

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

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

    5.解决方案

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

    1. static int rk8xx_pwrkey_probe(struct udevice *dev)
    2. {
    3. printf("rk8xx_pwrkey_probe\n");
    4. struct rk8xx_priv *rk8xx = dev_get_priv(dev->parent);
    5. struct dm_key_uclass_platdata *key = dev_get_uclass_platdata(dev);
    6. int fall_irq, rise_irq;
    7. if (!rk8xx->irq_chip) {
    8. printf("Failed to get parent irq chip\n");
    9. return -ENOENT;
    10. }
    11. fall_irq = virq_to_irq(rk8xx->irq_chip, RK8XX_IRQ_PWRON_FALL);
    12. if (fall_irq < 0) {
    13. printf("Failed to register pwron fall irq, ret=%d\n", fall_irq);
    14. return fall_irq;
    15. }
    16. rise_irq = virq_to_irq(rk8xx->irq_chip, RK8XX_IRQ_PWRON_RISE);
    17. if (rise_irq < 0) {
    18. printf("Failed to register pwron rise irq, ret=%d\n", rise_irq);
    19. return rise_irq;
    20. }
    21. key->name = "rk8xx_pwr";
    22. key->type = GPIO_KEY;
    23. key->code = KEY_POWER;
    24. key->skip_irq_init = 1;
    25. irq_install_handler(fall_irq, rk8xx_pwron_fall_handler, dev);
    26. irq_install_handler(rise_irq, rk8xx_pwron_rise_handler, dev);
    27. // irq_handler_enable(fall_irq);
    28. // irq_handler_enable(rise_irq);
    29. return 0;
    30. }

  • 相关阅读:
    mysql快速生成100万条测试数据脚本(InsCode AI 创作助手)
    抖音矩阵系统-60+自媒体平台一键发布-短视频获客系统
    《王者荣耀》怎么代理?今天又有个朋友问这个话题
    【场景化解决方案】构建医疗通讯录,“慧医钉”助力医院实现数字化管理
    c++ 学习之 初始化列表初始化属性
    实现一个 瀑布流 封装until 工具
    写作作业一
    Redis学习笔记——操作Hash
    Selenium上传文件有多少种方式?不信你有我全
    子组件向父组件传参的方式?
  • 原文地址:https://blog.csdn.net/professionalmcu/article/details/126183269