xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/boot_mode.c (revision a18cd24e5949ef8d329545aeb86a57cb537b7dbf)
1be55ced3SAndy Yan /*
2be55ced3SAndy Yan  * (C) Copyright 2016 Rockchip Electronics Co., Ltd
3be55ced3SAndy Yan  *
4be55ced3SAndy Yan  * SPDX-License-Identifier:     GPL-2.0+
5be55ced3SAndy Yan  */
6be55ced3SAndy Yan 
7be55ced3SAndy Yan #include <common.h>
8d04ada6cSJoseph Chen #include <boot_rkimg.h>
9d04ada6cSJoseph Chen #include <malloc.h>
10be55ced3SAndy Yan #include <asm/io.h>
11be55ced3SAndy Yan #include <asm/arch/boot_mode.h>
12efe731c2SAndy Yan 
13efe731c2SAndy Yan DECLARE_GLOBAL_DATA_PTR;
14be55ced3SAndy Yan 
15ea8b124aSJoseph Chen enum {
16ea8b124aSJoseph Chen 	PH = 0,	/* P: Priority, H: high, M: middle, L: low*/
17ea8b124aSJoseph Chen 	PM,
18ea8b124aSJoseph Chen 	PL,
19ea8b124aSJoseph Chen };
20ea8b124aSJoseph Chen 
21706ec1d4SJoseph Chen static u32 bcb_recovery_msg;
22706ec1d4SJoseph Chen 
23666b2d1fSJoseph Chen static int misc_require_recovery(u32 bcb_offset)
24666b2d1fSJoseph Chen {
25666b2d1fSJoseph Chen 	struct bootloader_message *bmsg;
26666b2d1fSJoseph Chen 	struct blk_desc *dev_desc;
27666b2d1fSJoseph Chen 	disk_partition_t part;
28666b2d1fSJoseph Chen 	int cnt, recovery = 0;
29666b2d1fSJoseph Chen 
30666b2d1fSJoseph Chen 	dev_desc = rockchip_get_bootdev();
31666b2d1fSJoseph Chen 	if (!dev_desc) {
32666b2d1fSJoseph Chen 		printf("dev_desc is NULL!\n");
33666b2d1fSJoseph Chen 		goto out;
34666b2d1fSJoseph Chen 	}
35666b2d1fSJoseph Chen 
36666b2d1fSJoseph Chen 	if (part_get_info_by_name(dev_desc, PART_MISC, &part) < 0) {
37666b2d1fSJoseph Chen 		printf("No misc partition\n");
38666b2d1fSJoseph Chen 		goto out;
39666b2d1fSJoseph Chen 	}
40666b2d1fSJoseph Chen 
41666b2d1fSJoseph Chen 	cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz);
42666b2d1fSJoseph Chen 	bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz);
431d2b0919SJason Zhu 	if (blk_dread(dev_desc, part.start + bcb_offset, cnt, bmsg) != cnt) {
44666b2d1fSJoseph Chen 		recovery = 0;
451d2b0919SJason Zhu 	} else {
46666b2d1fSJoseph Chen 		recovery = !strcmp(bmsg->command, "boot-recovery");
4760f07e75SJoseph Chen 		if (!strcmp(bmsg->recovery, "recovery\n--rk_fwupdate\n"))
48706ec1d4SJoseph Chen 			bcb_recovery_msg = BCB_MSG_RECOVERY_RK_FWUPDATE;
49*a18cd24eSJoseph Chen 		else if (!strcmp(bmsg->recovery, "recovery\n--factory_mode=whole") ||
50*a18cd24eSJoseph Chen 			 !strcmp(bmsg->recovery, "recovery\n--factory_mode=small"))
51*a18cd24eSJoseph Chen 			bcb_recovery_msg = BCB_MSG_RECOVERY_PCBA;
521d2b0919SJason Zhu 	}
53666b2d1fSJoseph Chen 
54666b2d1fSJoseph Chen 	free(bmsg);
55666b2d1fSJoseph Chen out:
56666b2d1fSJoseph Chen 	return recovery;
57666b2d1fSJoseph Chen }
58666b2d1fSJoseph Chen 
59706ec1d4SJoseph Chen int get_bcb_recovery_msg(void)
60706ec1d4SJoseph Chen {
61706ec1d4SJoseph Chen 	return bcb_recovery_msg;
62706ec1d4SJoseph Chen }
63706ec1d4SJoseph Chen 
64666b2d1fSJoseph Chen /*
65666b2d1fSJoseph Chen  * There are three ways to get reboot-mode:
66666b2d1fSJoseph Chen  *
67666b2d1fSJoseph Chen  * No1. Android BCB which is defined in misc.img (0KB or 16KB offset)
68666b2d1fSJoseph Chen  * No2. CONFIG_ROCKCHIP_BOOT_MODE_REG that supports "reboot xxx" commands
69666b2d1fSJoseph Chen  * No3. Env variable "reboot_mode" which is added by U-Boot
70666b2d1fSJoseph Chen  *
71666b2d1fSJoseph Chen  * Recovery mode from:
72666b2d1fSJoseph Chen  *	- Android BCB in misc.img
73666b2d1fSJoseph Chen  *	- "reboot recovery" command
74666b2d1fSJoseph Chen  *	- recovery key pressed without usb attach
75666b2d1fSJoseph Chen  */
76d04ada6cSJoseph Chen int rockchip_get_boot_mode(void)
7715e088eaSJoseph Chen {
78ea8b124aSJoseph Chen 	static int boot_mode[] =		/* static */
79ea8b124aSJoseph Chen 		{ -EINVAL, -EINVAL, -EINVAL };
80ea8b124aSJoseph Chen 	static int bcb_offset = -EINVAL;	/* static */
81666b2d1fSJoseph Chen 	uint32_t reg_boot_mode;
82666b2d1fSJoseph Chen 	char *env_reboot_mode;
83d04ada6cSJoseph Chen 	int clear_boot_reg = 0;
84d04ada6cSJoseph Chen #ifdef CONFIG_ANDROID_BOOT_IMAGE
853aaa96e8SJoseph Chen 	u32 offset = android_bcb_msg_sector_offset();
86d04ada6cSJoseph Chen #else
873aaa96e8SJoseph Chen 	u32 offset = BCB_MESSAGE_BLK_OFFSET;
88d04ada6cSJoseph Chen #endif
89ea8b124aSJoseph Chen 
90ea8b124aSJoseph Chen 	/*
91666b2d1fSJoseph Chen 	 * Env variable "reboot_mode" which is added by U-Boot, reading ever time.
92ea8b124aSJoseph Chen 	 */
93ea8b124aSJoseph Chen 	env_reboot_mode = env_get("reboot_mode");
94ea8b124aSJoseph Chen 	if (env_reboot_mode) {
95ea8b124aSJoseph Chen 		if (!strcmp(env_reboot_mode, "recovery-key")) {
96ea8b124aSJoseph Chen 			printf("boot mode: recovery (key)\n");
97ea8b124aSJoseph Chen 			return BOOT_MODE_RECOVERY;
98ea8b124aSJoseph Chen 		} else if (!strcmp(env_reboot_mode, "recovery-usb")) {
99ea8b124aSJoseph Chen 			printf("boot mode: recovery (usb)\n");
100ea8b124aSJoseph Chen 			return BOOT_MODE_RECOVERY;
101ea8b124aSJoseph Chen 		} else if (!strcmp(env_reboot_mode, "recovery")) {
102ea8b124aSJoseph Chen 			printf("boot mode: recovery (env)\n");
103ea8b124aSJoseph Chen 			return BOOT_MODE_RECOVERY;
104ea8b124aSJoseph Chen 		} else if (!strcmp(env_reboot_mode, "fastboot")) {
105ea8b124aSJoseph Chen 			printf("boot mode: fastboot\n");
106ea8b124aSJoseph Chen 			return BOOT_MODE_BOOTLOADER;
107ea8b124aSJoseph Chen 		}
108ea8b124aSJoseph Chen 	}
109ea8b124aSJoseph Chen 
1103aaa96e8SJoseph Chen 	/*
111666b2d1fSJoseph Chen 	 * Android BCB special handle:
112666b2d1fSJoseph Chen 	 *    Once the Android BCB offset changed, reinitalize "boot_mode[PM]".
1133aaa96e8SJoseph Chen 	 *
1143aaa96e8SJoseph Chen 	 * Background:
115666b2d1fSJoseph Chen 	 *    1. there are two Android BCB at the 0KB(google) and 16KB(rk)
116666b2d1fSJoseph Chen 	 *       offset in misc.img
117666b2d1fSJoseph Chen 	 *    2. Android image: return 0KB offset if image version >= 10,
118666b2d1fSJoseph Chen 	 *	 otherwise 16KB
119666b2d1fSJoseph Chen 	 *    3. Not Android image: return 16KB offset, eg: FIT image.
1203aaa96e8SJoseph Chen 	 *
121666b2d1fSJoseph Chen 	 * To handle the cases of 16KB and 0KB, we reinitial boot_mode[PM] once
122666b2d1fSJoseph Chen 	 * Android BCB is changed.
123ea8b124aSJoseph Chen 	 *
124666b2d1fSJoseph Chen 	 * PH and PL is from boot mode register and reading once.
125ea8b124aSJoseph Chen 	 * PM is from misc.img and should be updated if BCB offset is changed.
126ea8b124aSJoseph Chen 	 * Return the boot mode according to priority: PH > PM > PL.
1273aaa96e8SJoseph Chen 	 */
1283aaa96e8SJoseph Chen 	if (bcb_offset != offset) {
129ea8b124aSJoseph Chen 		boot_mode[PM] = -EINVAL;
1303aaa96e8SJoseph Chen 		bcb_offset = offset;
1313aaa96e8SJoseph Chen 	}
132d04ada6cSJoseph Chen 
133ea8b124aSJoseph Chen 	/* directly return if there is already valid mode */
134ea8b124aSJoseph Chen 	if (boot_mode[PH] != -EINVAL)
135ea8b124aSJoseph Chen 		return boot_mode[PH];
136ea8b124aSJoseph Chen 	else if (boot_mode[PM] != -EINVAL)
137ea8b124aSJoseph Chen 		return boot_mode[PM];
138ea8b124aSJoseph Chen 	else if (boot_mode[PL] != -EINVAL)
139ea8b124aSJoseph Chen 		return boot_mode[PL];
140d04ada6cSJoseph Chen 
141d04ada6cSJoseph Chen 	/*
142d04ada6cSJoseph Chen 	 * Boot mode priority
143d04ada6cSJoseph Chen 	 *
144d04ada6cSJoseph Chen 	 * Anyway, we should set download boot mode as the highest priority, so:
145d04ada6cSJoseph Chen 	 * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx.
146d04ada6cSJoseph Chen 	 */
147d04ada6cSJoseph Chen 	reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
148d04ada6cSJoseph Chen 	if (reg_boot_mode == BOOT_LOADER) {
149d04ada6cSJoseph Chen 		printf("boot mode: loader\n");
150ea8b124aSJoseph Chen 		boot_mode[PH] = BOOT_MODE_LOADER;
151d04ada6cSJoseph Chen 		clear_boot_reg = 1;
1520efe6414STony Xu 	} else if (reg_boot_mode == BOOT_DFU) {
1530efe6414STony Xu 		printf("boot mode: dfu\n");
1540efe6414STony Xu 		boot_mode[PH] = BOOT_MODE_DFU;
1550efe6414STony Xu 		clear_boot_reg = 1;
156d04ada6cSJoseph Chen 	} else if (reg_boot_mode == BOOT_FASTBOOT) {
157d04ada6cSJoseph Chen 		printf("boot mode: bootloader\n");
158ea8b124aSJoseph Chen 		boot_mode[PH] = BOOT_MODE_BOOTLOADER;
159d04ada6cSJoseph Chen 		clear_boot_reg = 1;
160666b2d1fSJoseph Chen 	} else if (misc_require_recovery(bcb_offset)) {
161d04ada6cSJoseph Chen 		printf("boot mode: recovery (misc)\n");
162ea8b124aSJoseph Chen 		boot_mode[PM] = BOOT_MODE_RECOVERY;
16328f66075SJoseph Chen 	} else {
164d04ada6cSJoseph Chen 		switch (reg_boot_mode) {
165d04ada6cSJoseph Chen 		case BOOT_NORMAL:
166d04ada6cSJoseph Chen 			printf("boot mode: normal\n");
167ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_NORMAL;
168d04ada6cSJoseph Chen 			clear_boot_reg = 1;
169d04ada6cSJoseph Chen 			break;
170d04ada6cSJoseph Chen 		case BOOT_RECOVERY:
171d04ada6cSJoseph Chen 			printf("boot mode: recovery (cmd)\n");
172ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_RECOVERY;
173d04ada6cSJoseph Chen 			clear_boot_reg = 1;
174d04ada6cSJoseph Chen 			break;
175d04ada6cSJoseph Chen 		case BOOT_UMS:
176d04ada6cSJoseph Chen 			printf("boot mode: ums\n");
177ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_UMS;
178d04ada6cSJoseph Chen 			clear_boot_reg = 1;
179d04ada6cSJoseph Chen 			break;
180d04ada6cSJoseph Chen 		case BOOT_CHARGING:
181d04ada6cSJoseph Chen 			printf("boot mode: charging\n");
182ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_CHARGING;
183d04ada6cSJoseph Chen 			clear_boot_reg = 1;
184d04ada6cSJoseph Chen 			break;
185d04ada6cSJoseph Chen 		case BOOT_PANIC:
186d04ada6cSJoseph Chen 			printf("boot mode: panic\n");
187ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_PANIC;
188d04ada6cSJoseph Chen 			break;
189d04ada6cSJoseph Chen 		case BOOT_WATCHDOG:
190d04ada6cSJoseph Chen 			printf("boot mode: watchdog\n");
191ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_WATCHDOG;
192d04ada6cSJoseph Chen 			break;
193d04ada6cSJoseph Chen 		default:
194d04ada6cSJoseph Chen 			printf("boot mode: None\n");
195ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_UNDEFINE;
19628f66075SJoseph Chen 		}
197692bb812SAndy Yan 	}
198d04ada6cSJoseph Chen 
199d04ada6cSJoseph Chen 	/*
200d04ada6cSJoseph Chen 	 * We don't clear boot mode reg when its value stands for the reboot
201d04ada6cSJoseph Chen 	 * reason or others(in the future), the kernel will need and clear it.
202d04ada6cSJoseph Chen 	 */
203d04ada6cSJoseph Chen 	if (clear_boot_reg)
204d04ada6cSJoseph Chen 		writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
205d04ada6cSJoseph Chen 
206ea8b124aSJoseph Chen 	if (boot_mode[PH] != -EINVAL)
207ea8b124aSJoseph Chen 		return boot_mode[PH];
208ea8b124aSJoseph Chen 	else if (boot_mode[PM] != -EINVAL)
209ea8b124aSJoseph Chen 		return boot_mode[PM];
210ea8b124aSJoseph Chen 	else
211ea8b124aSJoseph Chen 		return boot_mode[PL];
212692bb812SAndy Yan }
213692bb812SAndy Yan 
214be55ced3SAndy Yan int setup_boot_mode(void)
215be55ced3SAndy Yan {
21626362955SCody Xie 	char env_preboot[256] = {0};
217be55ced3SAndy Yan 
218d04ada6cSJoseph Chen 	switch (rockchip_get_boot_mode()) {
219b7195498SJoseph Chen 	case BOOT_MODE_BOOTLOADER:
220be55ced3SAndy Yan 		printf("enter fastboot!\n");
22126362955SCody Xie #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV)
22226362955SCody Xie 		snprintf(env_preboot, 256,
22326362955SCody Xie 				"setenv preboot; mmc dev %x; fastboot usb 0; ",
22426362955SCody Xie 				CONFIG_FASTBOOT_FLASH_MMC_DEV);
22526362955SCody Xie #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV)
22626362955SCody Xie 		snprintf(env_preboot, 256,
22726362955SCody Xie 				"setenv preboot; fastboot usb 0; ");
22826362955SCody Xie #endif
22926362955SCody Xie 		env_set("preboot", env_preboot);
230be55ced3SAndy Yan 		break;
231b7195498SJoseph Chen 	case BOOT_MODE_UMS:
232be55ced3SAndy Yan 		printf("enter UMS!\n");
233be55ced3SAndy Yan 		env_set("preboot", "setenv preboot; ums mmc 0");
234be55ced3SAndy Yan 		break;
2350efe6414STony Xu #if defined(CONFIG_CMD_DFU)
2360efe6414STony Xu 	case BOOT_MODE_DFU:
2370efe6414STony Xu 		printf("enter DFU!\n");
2380efe6414STony Xu 		env_set("preboot", "setenv preboot; dfu 0 ${devtype} ${devnum}; rbrom");
2390efe6414STony Xu 		break;
2400efe6414STony Xu #endif
241b7195498SJoseph Chen 	case BOOT_MODE_LOADER:
242be55ced3SAndy Yan 		printf("enter Rockusb!\n");
24336aa04d9SJoseph Chen 		env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom");
244be55ced3SAndy Yan 		break;
245b7195498SJoseph Chen 	case BOOT_MODE_CHARGING:
246885d92d4SJoseph Chen 		printf("enter charging!\n");
247885d92d4SJoseph Chen 		env_set("preboot", "setenv preboot; charge");
248885d92d4SJoseph Chen 		break;
249be55ced3SAndy Yan 	}
250be55ced3SAndy Yan 
251be55ced3SAndy Yan 	return 0;
252be55ced3SAndy Yan }
253