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