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