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