1be55ced3SAndy Yan /* 2be55ced3SAndy Yan * (C) Copyright 2016 Rockchip Electronics Co., Ltd 3be55ced3SAndy Yan * 4be55ced3SAndy Yan * SPDX-License-Identifier: GPL-2.0+ 5be55ced3SAndy Yan */ 6be55ced3SAndy Yan 7be55ced3SAndy Yan #include <common.h> 8d04ada6cSJoseph Chen #include <boot_rkimg.h> 9d04ada6cSJoseph Chen #include <malloc.h> 10be55ced3SAndy Yan #include <asm/io.h> 11be55ced3SAndy Yan #include <asm/arch/boot_mode.h> 12efe731c2SAndy Yan 13efe731c2SAndy Yan DECLARE_GLOBAL_DATA_PTR; 14be55ced3SAndy Yan 15ea8b124aSJoseph Chen enum { 16ea8b124aSJoseph Chen PH = 0, /* P: Priority, H: high, M: middle, L: low*/ 17ea8b124aSJoseph Chen PM, 18ea8b124aSJoseph Chen PL, 19ea8b124aSJoseph Chen }; 20ea8b124aSJoseph Chen 21706ec1d4SJoseph Chen static u32 bcb_recovery_msg; 22706ec1d4SJoseph Chen 23666b2d1fSJoseph Chen static int misc_require_recovery(u32 bcb_offset) 24666b2d1fSJoseph Chen { 25666b2d1fSJoseph Chen struct bootloader_message *bmsg; 26666b2d1fSJoseph Chen struct blk_desc *dev_desc; 27666b2d1fSJoseph Chen disk_partition_t part; 28666b2d1fSJoseph Chen int cnt, recovery = 0; 29666b2d1fSJoseph Chen 30666b2d1fSJoseph Chen dev_desc = rockchip_get_bootdev(); 31666b2d1fSJoseph Chen if (!dev_desc) { 32666b2d1fSJoseph Chen printf("dev_desc is NULL!\n"); 33666b2d1fSJoseph Chen goto out; 34666b2d1fSJoseph Chen } 35666b2d1fSJoseph Chen 36666b2d1fSJoseph Chen if (part_get_info_by_name(dev_desc, PART_MISC, &part) < 0) { 37666b2d1fSJoseph Chen printf("No misc partition\n"); 38666b2d1fSJoseph Chen goto out; 39666b2d1fSJoseph Chen } 40666b2d1fSJoseph Chen 41666b2d1fSJoseph Chen cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz); 42666b2d1fSJoseph Chen bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz); 431d2b0919SJason Zhu if (blk_dread(dev_desc, part.start + bcb_offset, cnt, bmsg) != cnt) { 44666b2d1fSJoseph Chen recovery = 0; 451d2b0919SJason Zhu } else { 46666b2d1fSJoseph Chen recovery = !strcmp(bmsg->command, "boot-recovery"); 4760f07e75SJoseph Chen if (!strcmp(bmsg->recovery, "recovery\n--rk_fwupdate\n")) 48706ec1d4SJoseph Chen bcb_recovery_msg = BCB_MSG_RECOVERY_RK_FWUPDATE; 49*a18cd24eSJoseph Chen else if (!strcmp(bmsg->recovery, "recovery\n--factory_mode=whole") || 50*a18cd24eSJoseph Chen !strcmp(bmsg->recovery, "recovery\n--factory_mode=small")) 51*a18cd24eSJoseph Chen bcb_recovery_msg = BCB_MSG_RECOVERY_PCBA; 521d2b0919SJason Zhu } 53666b2d1fSJoseph Chen 54666b2d1fSJoseph Chen free(bmsg); 55666b2d1fSJoseph Chen out: 56666b2d1fSJoseph Chen return recovery; 57666b2d1fSJoseph Chen } 58666b2d1fSJoseph Chen 59706ec1d4SJoseph Chen int get_bcb_recovery_msg(void) 60706ec1d4SJoseph Chen { 61706ec1d4SJoseph Chen return bcb_recovery_msg; 62706ec1d4SJoseph Chen } 63706ec1d4SJoseph Chen 64666b2d1fSJoseph Chen /* 65666b2d1fSJoseph Chen * There are three ways to get reboot-mode: 66666b2d1fSJoseph Chen * 67666b2d1fSJoseph Chen * No1. Android BCB which is defined in misc.img (0KB or 16KB offset) 68666b2d1fSJoseph Chen * No2. CONFIG_ROCKCHIP_BOOT_MODE_REG that supports "reboot xxx" commands 69666b2d1fSJoseph Chen * No3. Env variable "reboot_mode" which is added by U-Boot 70666b2d1fSJoseph Chen * 71666b2d1fSJoseph Chen * Recovery mode from: 72666b2d1fSJoseph Chen * - Android BCB in misc.img 73666b2d1fSJoseph Chen * - "reboot recovery" command 74666b2d1fSJoseph Chen * - recovery key pressed without usb attach 75666b2d1fSJoseph Chen */ 76d04ada6cSJoseph Chen int rockchip_get_boot_mode(void) 7715e088eaSJoseph Chen { 78ea8b124aSJoseph Chen static int boot_mode[] = /* static */ 79ea8b124aSJoseph Chen { -EINVAL, -EINVAL, -EINVAL }; 80ea8b124aSJoseph Chen static int bcb_offset = -EINVAL; /* static */ 81666b2d1fSJoseph Chen uint32_t reg_boot_mode; 82666b2d1fSJoseph Chen char *env_reboot_mode; 83d04ada6cSJoseph Chen int clear_boot_reg = 0; 84d04ada6cSJoseph Chen #ifdef CONFIG_ANDROID_BOOT_IMAGE 853aaa96e8SJoseph Chen u32 offset = android_bcb_msg_sector_offset(); 86d04ada6cSJoseph Chen #else 873aaa96e8SJoseph Chen u32 offset = BCB_MESSAGE_BLK_OFFSET; 88d04ada6cSJoseph Chen #endif 89ea8b124aSJoseph Chen 90ea8b124aSJoseph Chen /* 91666b2d1fSJoseph Chen * Env variable "reboot_mode" which is added by U-Boot, reading ever time. 92ea8b124aSJoseph Chen */ 93ea8b124aSJoseph Chen env_reboot_mode = env_get("reboot_mode"); 94ea8b124aSJoseph Chen if (env_reboot_mode) { 95ea8b124aSJoseph Chen if (!strcmp(env_reboot_mode, "recovery-key")) { 96ea8b124aSJoseph Chen printf("boot mode: recovery (key)\n"); 97ea8b124aSJoseph Chen return BOOT_MODE_RECOVERY; 98ea8b124aSJoseph Chen } else if (!strcmp(env_reboot_mode, "recovery-usb")) { 99ea8b124aSJoseph Chen printf("boot mode: recovery (usb)\n"); 100ea8b124aSJoseph Chen return BOOT_MODE_RECOVERY; 101ea8b124aSJoseph Chen } else if (!strcmp(env_reboot_mode, "recovery")) { 102ea8b124aSJoseph Chen printf("boot mode: recovery (env)\n"); 103ea8b124aSJoseph Chen return BOOT_MODE_RECOVERY; 104ea8b124aSJoseph Chen } else if (!strcmp(env_reboot_mode, "fastboot")) { 105ea8b124aSJoseph Chen printf("boot mode: fastboot\n"); 106ea8b124aSJoseph Chen return BOOT_MODE_BOOTLOADER; 107ea8b124aSJoseph Chen } 108ea8b124aSJoseph Chen } 109ea8b124aSJoseph Chen 1103aaa96e8SJoseph Chen /* 111666b2d1fSJoseph Chen * Android BCB special handle: 112666b2d1fSJoseph Chen * Once the Android BCB offset changed, reinitalize "boot_mode[PM]". 1133aaa96e8SJoseph Chen * 1143aaa96e8SJoseph Chen * Background: 115666b2d1fSJoseph Chen * 1. there are two Android BCB at the 0KB(google) and 16KB(rk) 116666b2d1fSJoseph Chen * offset in misc.img 117666b2d1fSJoseph Chen * 2. Android image: return 0KB offset if image version >= 10, 118666b2d1fSJoseph Chen * otherwise 16KB 119666b2d1fSJoseph Chen * 3. Not Android image: return 16KB offset, eg: FIT image. 1203aaa96e8SJoseph Chen * 121666b2d1fSJoseph Chen * To handle the cases of 16KB and 0KB, we reinitial boot_mode[PM] once 122666b2d1fSJoseph Chen * Android BCB is changed. 123ea8b124aSJoseph Chen * 124666b2d1fSJoseph Chen * PH and PL is from boot mode register and reading once. 125ea8b124aSJoseph Chen * PM is from misc.img and should be updated if BCB offset is changed. 126ea8b124aSJoseph Chen * Return the boot mode according to priority: PH > PM > PL. 1273aaa96e8SJoseph Chen */ 1283aaa96e8SJoseph Chen if (bcb_offset != offset) { 129ea8b124aSJoseph Chen boot_mode[PM] = -EINVAL; 1303aaa96e8SJoseph Chen bcb_offset = offset; 1313aaa96e8SJoseph Chen } 132d04ada6cSJoseph Chen 133ea8b124aSJoseph Chen /* directly return if there is already valid mode */ 134ea8b124aSJoseph Chen if (boot_mode[PH] != -EINVAL) 135ea8b124aSJoseph Chen return boot_mode[PH]; 136ea8b124aSJoseph Chen else if (boot_mode[PM] != -EINVAL) 137ea8b124aSJoseph Chen return boot_mode[PM]; 138ea8b124aSJoseph Chen else if (boot_mode[PL] != -EINVAL) 139ea8b124aSJoseph Chen return boot_mode[PL]; 140d04ada6cSJoseph Chen 141d04ada6cSJoseph Chen /* 142d04ada6cSJoseph Chen * Boot mode priority 143d04ada6cSJoseph Chen * 144d04ada6cSJoseph Chen * Anyway, we should set download boot mode as the highest priority, so: 145d04ada6cSJoseph Chen * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx. 146d04ada6cSJoseph Chen */ 147d04ada6cSJoseph Chen reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 148d04ada6cSJoseph Chen if (reg_boot_mode == BOOT_LOADER) { 149d04ada6cSJoseph Chen printf("boot mode: loader\n"); 150ea8b124aSJoseph Chen boot_mode[PH] = BOOT_MODE_LOADER; 151d04ada6cSJoseph Chen clear_boot_reg = 1; 1520efe6414STony Xu } else if (reg_boot_mode == BOOT_DFU) { 1530efe6414STony Xu printf("boot mode: dfu\n"); 1540efe6414STony Xu boot_mode[PH] = BOOT_MODE_DFU; 1550efe6414STony Xu clear_boot_reg = 1; 156d04ada6cSJoseph Chen } else if (reg_boot_mode == BOOT_FASTBOOT) { 157d04ada6cSJoseph Chen printf("boot mode: bootloader\n"); 158ea8b124aSJoseph Chen boot_mode[PH] = BOOT_MODE_BOOTLOADER; 159d04ada6cSJoseph Chen clear_boot_reg = 1; 160666b2d1fSJoseph Chen } else if (misc_require_recovery(bcb_offset)) { 161d04ada6cSJoseph Chen printf("boot mode: recovery (misc)\n"); 162ea8b124aSJoseph Chen boot_mode[PM] = BOOT_MODE_RECOVERY; 16328f66075SJoseph Chen } else { 164d04ada6cSJoseph Chen switch (reg_boot_mode) { 165d04ada6cSJoseph Chen case BOOT_NORMAL: 166d04ada6cSJoseph Chen printf("boot mode: normal\n"); 167ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_NORMAL; 168d04ada6cSJoseph Chen clear_boot_reg = 1; 169d04ada6cSJoseph Chen break; 170d04ada6cSJoseph Chen case BOOT_RECOVERY: 171d04ada6cSJoseph Chen printf("boot mode: recovery (cmd)\n"); 172ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_RECOVERY; 173d04ada6cSJoseph Chen clear_boot_reg = 1; 174d04ada6cSJoseph Chen break; 175d04ada6cSJoseph Chen case BOOT_UMS: 176d04ada6cSJoseph Chen printf("boot mode: ums\n"); 177ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_UMS; 178d04ada6cSJoseph Chen clear_boot_reg = 1; 179d04ada6cSJoseph Chen break; 180d04ada6cSJoseph Chen case BOOT_CHARGING: 181d04ada6cSJoseph Chen printf("boot mode: charging\n"); 182ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_CHARGING; 183d04ada6cSJoseph Chen clear_boot_reg = 1; 184d04ada6cSJoseph Chen break; 185d04ada6cSJoseph Chen case BOOT_PANIC: 186d04ada6cSJoseph Chen printf("boot mode: panic\n"); 187ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_PANIC; 188d04ada6cSJoseph Chen break; 189d04ada6cSJoseph Chen case BOOT_WATCHDOG: 190d04ada6cSJoseph Chen printf("boot mode: watchdog\n"); 191ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_WATCHDOG; 192d04ada6cSJoseph Chen break; 193d04ada6cSJoseph Chen default: 194d04ada6cSJoseph Chen printf("boot mode: None\n"); 195ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_UNDEFINE; 19628f66075SJoseph Chen } 197692bb812SAndy Yan } 198d04ada6cSJoseph Chen 199d04ada6cSJoseph Chen /* 200d04ada6cSJoseph Chen * We don't clear boot mode reg when its value stands for the reboot 201d04ada6cSJoseph Chen * reason or others(in the future), the kernel will need and clear it. 202d04ada6cSJoseph Chen */ 203d04ada6cSJoseph Chen if (clear_boot_reg) 204d04ada6cSJoseph Chen writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 205d04ada6cSJoseph Chen 206ea8b124aSJoseph Chen if (boot_mode[PH] != -EINVAL) 207ea8b124aSJoseph Chen return boot_mode[PH]; 208ea8b124aSJoseph Chen else if (boot_mode[PM] != -EINVAL) 209ea8b124aSJoseph Chen return boot_mode[PM]; 210ea8b124aSJoseph Chen else 211ea8b124aSJoseph Chen return boot_mode[PL]; 212692bb812SAndy Yan } 213692bb812SAndy Yan 214be55ced3SAndy Yan int setup_boot_mode(void) 215be55ced3SAndy Yan { 21626362955SCody Xie char env_preboot[256] = {0}; 217be55ced3SAndy Yan 218d04ada6cSJoseph Chen switch (rockchip_get_boot_mode()) { 219b7195498SJoseph Chen case BOOT_MODE_BOOTLOADER: 220be55ced3SAndy Yan printf("enter fastboot!\n"); 22126362955SCody Xie #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV) 22226362955SCody Xie snprintf(env_preboot, 256, 22326362955SCody Xie "setenv preboot; mmc dev %x; fastboot usb 0; ", 22426362955SCody Xie CONFIG_FASTBOOT_FLASH_MMC_DEV); 22526362955SCody Xie #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV) 22626362955SCody Xie snprintf(env_preboot, 256, 22726362955SCody Xie "setenv preboot; fastboot usb 0; "); 22826362955SCody Xie #endif 22926362955SCody Xie env_set("preboot", env_preboot); 230be55ced3SAndy Yan break; 231b7195498SJoseph Chen case BOOT_MODE_UMS: 232be55ced3SAndy Yan printf("enter UMS!\n"); 233be55ced3SAndy Yan env_set("preboot", "setenv preboot; ums mmc 0"); 234be55ced3SAndy Yan break; 2350efe6414STony Xu #if defined(CONFIG_CMD_DFU) 2360efe6414STony Xu case BOOT_MODE_DFU: 2370efe6414STony Xu printf("enter DFU!\n"); 2380efe6414STony Xu env_set("preboot", "setenv preboot; dfu 0 ${devtype} ${devnum}; rbrom"); 2390efe6414STony Xu break; 2400efe6414STony Xu #endif 241b7195498SJoseph Chen case BOOT_MODE_LOADER: 242be55ced3SAndy Yan printf("enter Rockusb!\n"); 24336aa04d9SJoseph Chen env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom"); 244be55ced3SAndy Yan break; 245b7195498SJoseph Chen case BOOT_MODE_CHARGING: 246885d92d4SJoseph Chen printf("enter charging!\n"); 247885d92d4SJoseph Chen env_set("preboot", "setenv preboot; charge"); 248885d92d4SJoseph Chen break; 249be55ced3SAndy Yan } 250be55ced3SAndy Yan 251be55ced3SAndy Yan return 0; 252be55ced3SAndy Yan } 253