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 enum { 16 PH = 0, /* P: Priority, H: high, M: middle, L: low*/ 17 PM, 18 PL, 19 }; 20 21 static int misc_require_recovery(u32 bcb_offset, int *bcb_recovery_msg) 22 { 23 struct bootloader_message *bmsg; 24 struct blk_desc *dev_desc; 25 disk_partition_t part; 26 int cnt, recovery = 0; 27 28 dev_desc = rockchip_get_bootdev(); 29 if (!dev_desc) { 30 printf("dev_desc is NULL!\n"); 31 goto out; 32 } 33 34 if (part_get_info_by_name(dev_desc, PART_MISC, &part) < 0) { 35 printf("No misc partition\n"); 36 goto out; 37 } 38 39 cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz); 40 bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz); 41 if (blk_dread(dev_desc, part.start + bcb_offset, cnt, bmsg) != cnt) { 42 recovery = 0; 43 } else { 44 recovery = !strcmp(bmsg->command, "boot-recovery"); 45 if (bcb_recovery_msg) { 46 if (!strcmp(bmsg->recovery, "recovery\n--rk_fwupdate\n")) 47 *bcb_recovery_msg = BCB_MSG_RECOVERY_RK_FWUPDATE; 48 else if (!strcmp(bmsg->recovery, "recovery\n--factory_mode=whole") || 49 !strcmp(bmsg->recovery, "recovery\n--factory_mode=small")) 50 *bcb_recovery_msg = BCB_MSG_RECOVERY_PCBA; 51 } 52 } 53 54 free(bmsg); 55 out: 56 return recovery; 57 } 58 59 int get_bcb_recovery_msg(void) 60 { 61 int bcb_recovery_msg = BCB_MSG_RECOVERY_NONE; 62 #ifdef CONFIG_ANDROID_BOOT_IMAGE 63 u32 bcb_offset = android_bcb_msg_sector_offset(); 64 #else 65 u32 bcb_offset = BCB_MESSAGE_BLK_OFFSET; 66 #endif 67 misc_require_recovery(bcb_offset, &bcb_recovery_msg); 68 69 return bcb_recovery_msg; 70 } 71 72 /* 73 * There are three ways to get reboot-mode: 74 * 75 * No1. Android BCB which is defined in misc.img (0KB or 16KB offset) 76 * No2. CONFIG_ROCKCHIP_BOOT_MODE_REG that supports "reboot xxx" commands 77 * No3. Env variable "reboot_mode" which is added by U-Boot 78 * 79 * Recovery mode from: 80 * - Android BCB in misc.img 81 * - "reboot recovery" command 82 * - recovery key pressed without usb attach 83 */ 84 int rockchip_get_boot_mode(void) 85 { 86 static int boot_mode[] = /* static */ 87 { -EINVAL, -EINVAL, -EINVAL }; 88 static int bcb_offset = -EINVAL; /* static */ 89 uint32_t reg_boot_mode; 90 char *env_reboot_mode; 91 int clear_boot_reg = 0; 92 int recovery_msg = 0; 93 #ifdef CONFIG_ANDROID_BOOT_IMAGE 94 u32 offset = android_bcb_msg_sector_offset(); 95 #else 96 u32 offset = BCB_MESSAGE_BLK_OFFSET; 97 #endif 98 99 /* 100 * Env variable "reboot_mode" which is added by U-Boot, reading ever time. 101 */ 102 env_reboot_mode = env_get("reboot_mode"); 103 if (env_reboot_mode) { 104 if (!strcmp(env_reboot_mode, "recovery-key")) { 105 printf("boot mode: recovery (key)\n"); 106 return BOOT_MODE_RECOVERY; 107 } else if (!strcmp(env_reboot_mode, "recovery-usb")) { 108 printf("boot mode: recovery (usb)\n"); 109 return BOOT_MODE_RECOVERY; 110 } else if (!strcmp(env_reboot_mode, "recovery")) { 111 printf("boot mode: recovery (env)\n"); 112 return BOOT_MODE_RECOVERY; 113 } else if (!strcmp(env_reboot_mode, "fastboot")) { 114 printf("boot mode: fastboot\n"); 115 return BOOT_MODE_BOOTLOADER; 116 } else if (!strcmp(env_reboot_mode, "normal")) { 117 printf("boot mode: normal(env)\n"); 118 return BOOT_MODE_NORMAL; 119 } 120 } 121 122 /* 123 * Android BCB special handle: 124 * Once the Android BCB offset changed, reinitalize "boot_mode[PM]". 125 * 126 * Background: 127 * 1. there are two Android BCB at the 0KB(google) and 16KB(rk) 128 * offset in misc.img 129 * 2. Android image: return 0KB offset if image version >= 10, 130 * otherwise 16KB 131 * 3. Not Android image: return 16KB offset, eg: FIT image. 132 * 133 * To handle the cases of 16KB and 0KB, we reinitial boot_mode[PM] once 134 * Android BCB is changed. 135 * 136 * PH and PL is from boot mode register and reading once. 137 * PM is from misc.img and should be updated if BCB offset is changed. 138 * Return the boot mode according to priority: PH > PM > PL. 139 */ 140 if (bcb_offset != offset) { 141 boot_mode[PM] = -EINVAL; 142 bcb_offset = offset; 143 } 144 145 /* directly return if there is already valid mode */ 146 if (boot_mode[PH] != -EINVAL) 147 return boot_mode[PH]; 148 else if (boot_mode[PM] != -EINVAL) 149 return boot_mode[PM]; 150 else if (boot_mode[PL] != -EINVAL) 151 return boot_mode[PL]; 152 153 /* 154 * Boot mode priority 155 * 156 * Anyway, we should set download boot mode as the highest priority, so: 157 * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx. 158 */ 159 reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 160 if (reg_boot_mode == BOOT_LOADER) { 161 printf("boot mode: loader\n"); 162 boot_mode[PH] = BOOT_MODE_LOADER; 163 clear_boot_reg = 1; 164 } else if (reg_boot_mode == BOOT_DFU) { 165 printf("boot mode: dfu\n"); 166 boot_mode[PH] = BOOT_MODE_DFU; 167 clear_boot_reg = 1; 168 } else if (reg_boot_mode == BOOT_FASTBOOT) { 169 printf("boot mode: bootloader\n"); 170 boot_mode[PH] = BOOT_MODE_BOOTLOADER; 171 clear_boot_reg = 1; 172 } else if (misc_require_recovery(bcb_offset, &recovery_msg)) { 173 printf("boot mode: recovery (misc)\n"); 174 boot_mode[PM] = BOOT_MODE_RECOVERY; 175 } else { 176 switch (reg_boot_mode) { 177 case BOOT_NORMAL: 178 printf("boot mode: normal\n"); 179 boot_mode[PL] = BOOT_MODE_NORMAL; 180 clear_boot_reg = 1; 181 break; 182 case BOOT_RECOVERY: 183 printf("boot mode: recovery (cmd)\n"); 184 boot_mode[PL] = BOOT_MODE_RECOVERY; 185 clear_boot_reg = 1; 186 break; 187 case BOOT_UMS: 188 printf("boot mode: ums\n"); 189 boot_mode[PL] = BOOT_MODE_UMS; 190 clear_boot_reg = 1; 191 break; 192 case BOOT_CHARGING: 193 printf("boot mode: charging\n"); 194 boot_mode[PL] = BOOT_MODE_CHARGING; 195 clear_boot_reg = 1; 196 break; 197 case BOOT_PANIC: 198 printf("boot mode: panic\n"); 199 boot_mode[PL] = BOOT_MODE_PANIC; 200 break; 201 case BOOT_WATCHDOG: 202 printf("boot mode: watchdog\n"); 203 boot_mode[PL] = BOOT_MODE_WATCHDOG; 204 break; 205 case BOOT_QUIESCENT: 206 printf("boot mode: quiescent\n"); 207 boot_mode[PL] = BOOT_MODE_QUIESCENT; 208 break; 209 default: 210 printf("boot mode: None\n"); 211 boot_mode[PL] = BOOT_MODE_UNDEFINE; 212 } 213 } 214 215 /* 216 * We don't clear boot mode reg when its value stands for the reboot 217 * reason or others(in the future), the kernel will need and clear it. 218 */ 219 if (clear_boot_reg) 220 writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 221 222 if (boot_mode[PH] != -EINVAL) 223 return boot_mode[PH]; 224 else if (boot_mode[PM] != -EINVAL) 225 return boot_mode[PM]; 226 else 227 return boot_mode[PL]; 228 } 229 230 int setup_boot_mode(void) 231 { 232 char env_preboot[256] = {0}; 233 234 switch (rockchip_get_boot_mode()) { 235 case BOOT_MODE_BOOTLOADER: 236 printf("enter fastboot!\n"); 237 #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV) 238 snprintf(env_preboot, 256, 239 "setenv preboot; mmc dev %x; fastboot usb 0; ", 240 CONFIG_FASTBOOT_FLASH_MMC_DEV); 241 #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV) 242 snprintf(env_preboot, 256, 243 "setenv preboot; fastboot usb 0; "); 244 #endif 245 env_set("preboot", env_preboot); 246 break; 247 case BOOT_MODE_UMS: 248 printf("enter UMS!\n"); 249 env_set("preboot", "setenv preboot; ums mmc 0"); 250 break; 251 #if defined(CONFIG_CMD_DFU) 252 case BOOT_MODE_DFU: 253 printf("enter DFU!\n"); 254 env_set("preboot", "setenv preboot; dfu 0 ${devtype} ${devnum}; rbrom"); 255 break; 256 #endif 257 case BOOT_MODE_LOADER: 258 printf("enter Rockusb!\n"); 259 env_set("preboot", "setenv preboot; download"); 260 run_command("download", 0); 261 break; 262 case BOOT_MODE_CHARGING: 263 printf("enter charging!\n"); 264 env_set("preboot", "setenv preboot; charge"); 265 break; 266 } 267 268 return 0; 269 } 270