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 } 117 } 118 119 /* 120 * Android BCB special handle: 121 * Once the Android BCB offset changed, reinitalize "boot_mode[PM]". 122 * 123 * Background: 124 * 1. there are two Android BCB at the 0KB(google) and 16KB(rk) 125 * offset in misc.img 126 * 2. Android image: return 0KB offset if image version >= 10, 127 * otherwise 16KB 128 * 3. Not Android image: return 16KB offset, eg: FIT image. 129 * 130 * To handle the cases of 16KB and 0KB, we reinitial boot_mode[PM] once 131 * Android BCB is changed. 132 * 133 * PH and PL is from boot mode register and reading once. 134 * PM is from misc.img and should be updated if BCB offset is changed. 135 * Return the boot mode according to priority: PH > PM > PL. 136 */ 137 if (bcb_offset != offset) { 138 boot_mode[PM] = -EINVAL; 139 bcb_offset = offset; 140 } 141 142 /* directly return if there is already valid mode */ 143 if (boot_mode[PH] != -EINVAL) 144 return boot_mode[PH]; 145 else if (boot_mode[PM] != -EINVAL) 146 return boot_mode[PM]; 147 else if (boot_mode[PL] != -EINVAL) 148 return boot_mode[PL]; 149 150 /* 151 * Boot mode priority 152 * 153 * Anyway, we should set download boot mode as the highest priority, so: 154 * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx. 155 */ 156 reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 157 if (reg_boot_mode == BOOT_LOADER) { 158 printf("boot mode: loader\n"); 159 boot_mode[PH] = BOOT_MODE_LOADER; 160 clear_boot_reg = 1; 161 } else if (reg_boot_mode == BOOT_DFU) { 162 printf("boot mode: dfu\n"); 163 boot_mode[PH] = BOOT_MODE_DFU; 164 clear_boot_reg = 1; 165 } else if (reg_boot_mode == BOOT_FASTBOOT) { 166 printf("boot mode: bootloader\n"); 167 boot_mode[PH] = BOOT_MODE_BOOTLOADER; 168 clear_boot_reg = 1; 169 } else if (misc_require_recovery(bcb_offset, &recovery_msg)) { 170 printf("boot mode: recovery (misc)\n"); 171 boot_mode[PM] = BOOT_MODE_RECOVERY; 172 } else { 173 switch (reg_boot_mode) { 174 case BOOT_NORMAL: 175 printf("boot mode: normal\n"); 176 boot_mode[PL] = BOOT_MODE_NORMAL; 177 clear_boot_reg = 1; 178 break; 179 case BOOT_RECOVERY: 180 printf("boot mode: recovery (cmd)\n"); 181 boot_mode[PL] = BOOT_MODE_RECOVERY; 182 clear_boot_reg = 1; 183 break; 184 case BOOT_UMS: 185 printf("boot mode: ums\n"); 186 boot_mode[PL] = BOOT_MODE_UMS; 187 clear_boot_reg = 1; 188 break; 189 case BOOT_CHARGING: 190 printf("boot mode: charging\n"); 191 boot_mode[PL] = BOOT_MODE_CHARGING; 192 clear_boot_reg = 1; 193 break; 194 case BOOT_PANIC: 195 printf("boot mode: panic\n"); 196 boot_mode[PL] = BOOT_MODE_PANIC; 197 break; 198 case BOOT_WATCHDOG: 199 printf("boot mode: watchdog\n"); 200 boot_mode[PL] = BOOT_MODE_WATCHDOG; 201 break; 202 default: 203 printf("boot mode: None\n"); 204 boot_mode[PL] = BOOT_MODE_UNDEFINE; 205 } 206 } 207 208 /* 209 * We don't clear boot mode reg when its value stands for the reboot 210 * reason or others(in the future), the kernel will need and clear it. 211 */ 212 if (clear_boot_reg) 213 writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); 214 215 if (boot_mode[PH] != -EINVAL) 216 return boot_mode[PH]; 217 else if (boot_mode[PM] != -EINVAL) 218 return boot_mode[PM]; 219 else 220 return boot_mode[PL]; 221 } 222 223 int setup_boot_mode(void) 224 { 225 char env_preboot[256] = {0}; 226 227 switch (rockchip_get_boot_mode()) { 228 case BOOT_MODE_BOOTLOADER: 229 printf("enter fastboot!\n"); 230 #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV) 231 snprintf(env_preboot, 256, 232 "setenv preboot; mmc dev %x; fastboot usb 0; ", 233 CONFIG_FASTBOOT_FLASH_MMC_DEV); 234 #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV) 235 snprintf(env_preboot, 256, 236 "setenv preboot; fastboot usb 0; "); 237 #endif 238 env_set("preboot", env_preboot); 239 break; 240 case BOOT_MODE_UMS: 241 printf("enter UMS!\n"); 242 env_set("preboot", "setenv preboot; ums mmc 0"); 243 break; 244 #if defined(CONFIG_CMD_DFU) 245 case BOOT_MODE_DFU: 246 printf("enter DFU!\n"); 247 env_set("preboot", "setenv preboot; dfu 0 ${devtype} ${devnum}; rbrom"); 248 break; 249 #endif 250 case BOOT_MODE_LOADER: 251 printf("enter Rockusb!\n"); 252 env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom"); 253 break; 254 case BOOT_MODE_CHARGING: 255 printf("enter charging!\n"); 256 env_set("preboot", "setenv preboot; charge"); 257 break; 258 } 259 260 return 0; 261 } 262