xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/boot_mode.c (revision f36ea2f6e17621c4d9dd97c4dbfab62d03d061df)
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