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