/* * (C) Copyright 2016 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* * Generally, we have 3 ways to get reboot mode: * * 1. from bootloader_message which is defined in MISC partition; * 2. from CONFIG_ROCKCHIP_BOOT_MODE_REG which supports "reboot xxx" commands; * 3. from env "reboot_mode" which is added by U-Boot code(currently only when * recovery key pressed); * * 1st and 2nd cases are static determined at system start and we check it once, * while 3th case is dynamically added by U-Boot code, so we have to check it * everytime. * * Recovery mode from: * - MISC partition; * - "reboot recovery" command; * - recovery key pressed without usb attach; */ int rockchip_get_boot_mode(void) { struct bootloader_message *bmsg = NULL; struct blk_desc *dev_desc; disk_partition_t part_info; uint32_t reg_boot_mode; char *env_reboot_mode; static int boot_mode = -1; /* static */ static int bcb_offset = -1; /* static */ int clear_boot_reg = 0; int ret, cnt; #ifdef CONFIG_ANDROID_BOOT_IMAGE u32 offset = android_bcb_msg_sector_offset(); #else u32 offset = BCB_MESSAGE_BLK_OFFSET; #endif /* * Special handle: * Once the BCB offset changes, reinitalize "boot_mode". * * Background: * 1. there are two Android BCB at the 0x00 and 0x20 offset in * misc.img to compatible legacy(0x20) SDK. * 2. android_bcb_msg_sector_offset() is for android image: * return 0x20 if image version < 10, otherwise 0x00. * 3. If not android image, BCB at 0x20 is the valid one. * * U-Boot can support booting both FIT & Android image, if FIT * boot flow enters here early than Android, the "boot_mode" is * set as BOOT_MODE_RECOVERY according to BCB at 0x20 offset. * After that, this function always return static variable "boot_mode" * as BOOT_MODE_RECOVERY even android(>=10) boot flow enter here. */ if (bcb_offset != offset) { boot_mode = -1; bcb_offset = offset; } /* * Here, we mainly check for: * In rockusb_download(), that recovery key is pressed without * USB attach will do env_set("reboot_mode", "recovery"); */ env_reboot_mode = env_get("reboot_mode"); if (env_reboot_mode) { if (!strcmp(env_reboot_mode, "recovery-key")) { boot_mode = BOOT_MODE_RECOVERY; printf("boot mode: recovery (key)\n"); } else if (!strcmp(env_reboot_mode, "recovery-usb")) { boot_mode = BOOT_MODE_RECOVERY; printf("boot mode: recovery (usb)\n"); } else if (!strcmp(env_reboot_mode, "recovery")) { boot_mode = BOOT_MODE_RECOVERY; printf("boot mode: recovery(env)\n"); } else if (!strcmp(env_reboot_mode, "fastboot")) { boot_mode = BOOT_MODE_BOOTLOADER; printf("boot mode: fastboot\n"); } } if (boot_mode != -1) return boot_mode; dev_desc = rockchip_get_bootdev(); if (!dev_desc) { printf("dev_desc is NULL!\n"); return -ENODEV; } ret = part_get_info_by_name(dev_desc, PART_MISC, &part_info); if (ret < 0) { printf("No misc partition\n"); goto fallback; } cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz); bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz); ret = blk_dread(dev_desc, part_info.start + bcb_offset, cnt, bmsg); if (ret != cnt) { free(bmsg); return -EIO; } fallback: /* * Boot mode priority * * Anyway, we should set download boot mode as the highest priority, so: * * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx. */ reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); if (reg_boot_mode == BOOT_LOADER) { printf("boot mode: loader\n"); boot_mode = BOOT_MODE_LOADER; clear_boot_reg = 1; } else if (reg_boot_mode == BOOT_FASTBOOT) { printf("boot mode: bootloader\n"); boot_mode = BOOT_MODE_BOOTLOADER; clear_boot_reg = 1; } else if (bmsg && !strcmp(bmsg->command, "boot-recovery")) { printf("boot mode: recovery (misc)\n"); boot_mode = BOOT_MODE_RECOVERY; clear_boot_reg = 1; } else { switch (reg_boot_mode) { case BOOT_NORMAL: printf("boot mode: normal\n"); boot_mode = BOOT_MODE_NORMAL; clear_boot_reg = 1; break; case BOOT_RECOVERY: printf("boot mode: recovery (cmd)\n"); boot_mode = BOOT_MODE_RECOVERY; clear_boot_reg = 1; break; case BOOT_UMS: printf("boot mode: ums\n"); boot_mode = BOOT_MODE_UMS; clear_boot_reg = 1; break; case BOOT_CHARGING: printf("boot mode: charging\n"); boot_mode = BOOT_MODE_CHARGING; clear_boot_reg = 1; break; case BOOT_PANIC: printf("boot mode: panic\n"); boot_mode = BOOT_MODE_PANIC; break; case BOOT_WATCHDOG: printf("boot mode: watchdog\n"); boot_mode = BOOT_MODE_WATCHDOG; break; default: printf("boot mode: None\n"); boot_mode = BOOT_MODE_UNDEFINE; } } /* * We don't clear boot mode reg when its value stands for the reboot * reason or others(in the future), the kernel will need and clear it. */ if (clear_boot_reg) writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); return boot_mode; } int setup_boot_mode(void) { char env_preboot[256] = {0}; switch (rockchip_get_boot_mode()) { case BOOT_MODE_BOOTLOADER: printf("enter fastboot!\n"); #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV) snprintf(env_preboot, 256, "setenv preboot; mmc dev %x; fastboot usb 0; ", CONFIG_FASTBOOT_FLASH_MMC_DEV); #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV) snprintf(env_preboot, 256, "setenv preboot; fastboot usb 0; "); #endif env_set("preboot", env_preboot); break; case BOOT_MODE_UMS: printf("enter UMS!\n"); env_set("preboot", "setenv preboot; ums mmc 0"); break; case BOOT_MODE_LOADER: printf("enter Rockusb!\n"); env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom"); break; case BOOT_MODE_CHARGING: printf("enter charging!\n"); env_set("preboot", "setenv preboot; charge"); break; } return 0; }