1 /* 2 * (C) Copyright 2016 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <boot_rkimg.h> 9 #include <malloc.h> 10 #include <asm/io.h> 11 #include <asm/arch/boot_mode.h> 12 13 DECLARE_GLOBAL_DATA_PTR; 14 15 /* 16 * Generally, we have 3 ways to get reboot mode: 17 * 18 * 1. from bootloader_message which is defined in MISC partition; 19 * 2. from CONFIG_ROCKCHIP_BOOT_MODE_REG which supports "reboot xxx" commands; 20 * 3. from env "reboot_mode" which is added by U-Boot code(currently only when 21 * recovery key pressed); 22 * 23 * 1st and 2nd cases are static determined at system start and we check it once, 24 * while 3th case is dynamically added by U-Boot code, so we have to check it 25 * everytime. 26 * 27 * Recovery mode from: 28 * - MISC partition; 29 * - "reboot recovery" command; 30 * - recovery key pressed without usb attach; 31 */ 32 int rockchip_get_boot_mode(void) 33 { 34 struct bootloader_message *bmsg = NULL; 35 struct blk_desc *dev_desc; 36 disk_partition_t part_info; 37 uint32_t reg_boot_mode; 38 char *env_reboot_mode; 39 static int boot_mode = -1; /* static */ 40 static int bcb_offset = -1; /* static */ 41 int clear_boot_reg = 0; 42 int ret, cnt; 43 #ifdef CONFIG_ANDROID_BOOT_IMAGE 44 u32 offset = android_bcb_msg_sector_offset(); 45 #else 46 u32 offset = BCB_MESSAGE_BLK_OFFSET; 47 #endif 48 /* 49 * Special handle: 50 * Once the BCB offset changes, reinitalize "boot_mode". 51 * 52 * Background: 53 * 1. there are two Android BCB at the 0x00 and 0x20 offset in 54 * misc.img to compatible legacy(0x20) SDK. 55 * 2. android_bcb_msg_sector_offset() is for android image: 56 * return 0x20 if image version < 10, otherwise 0x00. 57 * 3. If not android image, BCB at 0x20 is the valid one. 58 * 59 * U-Boot can support booting both FIT & Android image, if FIT 60 * boot flow enters here early than Android, the "boot_mode" is 61 * set as BOOT_MODE_RECOVERY according to BCB at 0x20 offset. 62 * After that, this function always return static variable "boot_mode" 63 * as BOOT_MODE_RECOVERY even android(>=10) boot flow enter here. 64 */ 65 if (bcb_offset != offset) { 66 boot_mode = -1; 67 bcb_offset = offset; 68 } 69 70 /* 71 * Here, we mainly check for: 72 * In rockusb_download(), that recovery key is pressed without 73 * USB attach will do env_set("reboot_mode", "recovery"); 74 */ 75 env_reboot_mode = env_get("reboot_mode"); 76 if (env_reboot_mode) { 77 if (!strcmp(env_reboot_mode, "recovery-key")) { 78 boot_mode = BOOT_MODE_RECOVERY; 79 printf("boot mode: recovery (key)\n"); 80 } else if (!strcmp(env_reboot_mode, "recovery-usb")) { 81 boot_mode = BOOT_MODE_RECOVERY; 82 printf("boot mode: recovery (usb)\n"); 83 } else if (!strcmp(env_reboot_mode, "recovery")) { 84 boot_mode = BOOT_MODE_RECOVERY; 85 printf("boot mode: recovery(env)\n"); 86 } else if (!strcmp(env_reboot_mode, "fastboot")) { 87 boot_mode = BOOT_MODE_BOOTLOADER; 88 printf("boot mode: fastboot\n"); 89 } 90 } 91 92 if (boot_mode != -1) 93 return boot_mode; 94 95 dev_desc = rockchip_get_bootdev(); 96 if (!dev_desc) { 97 printf("dev_desc is NULL!\n"); 98 return -ENODEV; 99 } 100 101 ret = part_get_info_by_name(dev_desc, PART_MISC, &part_info); 102 if (ret < 0) { 103 printf("No misc partition\n"); 104 goto fallback; 105 } 106 107 cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz); 108 bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz); 109 ret = blk_dread(dev_desc, 110 part_info.start + bcb_offset, 111 cnt, bmsg); 112 if (ret != cnt) { 113 free(bmsg); 114 return -EIO; 115 } 116 117 fallback: 118 /* 119 * Boot mode priority 120 * 121 * Anyway, we should set download boot mode as the highest priority, so: 122 * 123 * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx. 124 */ 125 reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 126 if (reg_boot_mode == BOOT_LOADER) { 127 printf("boot mode: loader\n"); 128 boot_mode = BOOT_MODE_LOADER; 129 clear_boot_reg = 1; 130 } else if (reg_boot_mode == BOOT_FASTBOOT) { 131 printf("boot mode: bootloader\n"); 132 boot_mode = BOOT_MODE_BOOTLOADER; 133 clear_boot_reg = 1; 134 } else if (bmsg && !strcmp(bmsg->command, "boot-recovery")) { 135 printf("boot mode: recovery (misc)\n"); 136 boot_mode = BOOT_MODE_RECOVERY; 137 clear_boot_reg = 1; 138 } else { 139 switch (reg_boot_mode) { 140 case BOOT_NORMAL: 141 printf("boot mode: normal\n"); 142 boot_mode = BOOT_MODE_NORMAL; 143 clear_boot_reg = 1; 144 break; 145 case BOOT_RECOVERY: 146 printf("boot mode: recovery (cmd)\n"); 147 boot_mode = BOOT_MODE_RECOVERY; 148 clear_boot_reg = 1; 149 break; 150 case BOOT_UMS: 151 printf("boot mode: ums\n"); 152 boot_mode = BOOT_MODE_UMS; 153 clear_boot_reg = 1; 154 break; 155 case BOOT_CHARGING: 156 printf("boot mode: charging\n"); 157 boot_mode = BOOT_MODE_CHARGING; 158 clear_boot_reg = 1; 159 break; 160 case BOOT_PANIC: 161 printf("boot mode: panic\n"); 162 boot_mode = BOOT_MODE_PANIC; 163 break; 164 case BOOT_WATCHDOG: 165 printf("boot mode: watchdog\n"); 166 boot_mode = BOOT_MODE_WATCHDOG; 167 break; 168 default: 169 printf("boot mode: None\n"); 170 boot_mode = BOOT_MODE_UNDEFINE; 171 } 172 } 173 174 /* 175 * We don't clear boot mode reg when its value stands for the reboot 176 * reason or others(in the future), the kernel will need and clear it. 177 */ 178 if (clear_boot_reg) 179 writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 180 181 return boot_mode; 182 } 183 184 int setup_boot_mode(void) 185 { 186 char env_preboot[256] = {0}; 187 188 switch (rockchip_get_boot_mode()) { 189 case BOOT_MODE_BOOTLOADER: 190 printf("enter fastboot!\n"); 191 #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV) 192 snprintf(env_preboot, 256, 193 "setenv preboot; mmc dev %x; fastboot usb 0; ", 194 CONFIG_FASTBOOT_FLASH_MMC_DEV); 195 #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV) 196 snprintf(env_preboot, 256, 197 "setenv preboot; fastboot usb 0; "); 198 #endif 199 env_set("preboot", env_preboot); 200 break; 201 case BOOT_MODE_UMS: 202 printf("enter UMS!\n"); 203 env_set("preboot", "setenv preboot; ums mmc 0"); 204 break; 205 case BOOT_MODE_LOADER: 206 printf("enter Rockusb!\n"); 207 env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom"); 208 break; 209 case BOOT_MODE_CHARGING: 210 printf("enter charging!\n"); 211 env_set("preboot", "setenv preboot; charge"); 212 break; 213 } 214 215 return 0; 216 } 217