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
misc_require_recovery(u32 bcb_offset,int * bcb_recovery_msg)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
get_bcb_recovery_msg(void)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 */
rockchip_get_boot_mode(void)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
setup_boot_mode(void)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