xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/boot_mode.c (revision 666b2d1f244aa4569b3b7b7c6a8d606c20bcb3d9)
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 
21*666b2d1fSJoseph Chen static int misc_require_recovery(u32 bcb_offset)
22*666b2d1fSJoseph Chen {
23*666b2d1fSJoseph Chen 	struct bootloader_message *bmsg;
24*666b2d1fSJoseph Chen 	struct blk_desc *dev_desc;
25*666b2d1fSJoseph Chen 	disk_partition_t part;
26*666b2d1fSJoseph Chen 	int cnt, recovery = 0;
27*666b2d1fSJoseph Chen 
28*666b2d1fSJoseph Chen 	dev_desc = rockchip_get_bootdev();
29*666b2d1fSJoseph Chen 	if (!dev_desc) {
30*666b2d1fSJoseph Chen 		printf("dev_desc is NULL!\n");
31*666b2d1fSJoseph Chen 		goto out;
32*666b2d1fSJoseph Chen 	}
33*666b2d1fSJoseph Chen 
34*666b2d1fSJoseph Chen 	if (part_get_info_by_name(dev_desc, PART_MISC, &part) < 0) {
35*666b2d1fSJoseph Chen 		printf("No misc partition\n");
36*666b2d1fSJoseph Chen 		goto out;
37*666b2d1fSJoseph Chen 	}
38*666b2d1fSJoseph Chen 
39*666b2d1fSJoseph Chen 	cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz);
40*666b2d1fSJoseph Chen 	bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz);
41*666b2d1fSJoseph Chen 	if (blk_dread(dev_desc, part.start + bcb_offset, cnt, bmsg) != cnt)
42*666b2d1fSJoseph Chen 		recovery = 0;
43*666b2d1fSJoseph Chen 	else
44*666b2d1fSJoseph Chen 		recovery = !strcmp(bmsg->command, "boot-recovery");
45*666b2d1fSJoseph Chen 
46*666b2d1fSJoseph Chen 	free(bmsg);
47*666b2d1fSJoseph Chen out:
48*666b2d1fSJoseph Chen 	return recovery;
49*666b2d1fSJoseph Chen }
50*666b2d1fSJoseph Chen 
51*666b2d1fSJoseph Chen /*
52*666b2d1fSJoseph Chen  * There are three ways to get reboot-mode:
53*666b2d1fSJoseph Chen  *
54*666b2d1fSJoseph Chen  * No1. Android BCB which is defined in misc.img (0KB or 16KB offset)
55*666b2d1fSJoseph Chen  * No2. CONFIG_ROCKCHIP_BOOT_MODE_REG that supports "reboot xxx" commands
56*666b2d1fSJoseph Chen  * No3. Env variable "reboot_mode" which is added by U-Boot
57*666b2d1fSJoseph Chen  *
58*666b2d1fSJoseph Chen  * Recovery mode from:
59*666b2d1fSJoseph Chen  *	- Android BCB in misc.img
60*666b2d1fSJoseph Chen  *	- "reboot recovery" command
61*666b2d1fSJoseph Chen  *	- recovery key pressed without usb attach
62*666b2d1fSJoseph Chen  */
63d04ada6cSJoseph Chen int rockchip_get_boot_mode(void)
6415e088eaSJoseph Chen {
65ea8b124aSJoseph Chen 	static int boot_mode[] =		/* static */
66ea8b124aSJoseph Chen 		{ -EINVAL, -EINVAL, -EINVAL };
67ea8b124aSJoseph Chen 	static int bcb_offset = -EINVAL;	/* static */
68*666b2d1fSJoseph Chen 	uint32_t reg_boot_mode;
69*666b2d1fSJoseph Chen 	char *env_reboot_mode;
70d04ada6cSJoseph Chen 	int clear_boot_reg = 0;
71d04ada6cSJoseph Chen #ifdef CONFIG_ANDROID_BOOT_IMAGE
723aaa96e8SJoseph Chen 	u32 offset = android_bcb_msg_sector_offset();
73d04ada6cSJoseph Chen #else
743aaa96e8SJoseph Chen 	u32 offset = BCB_MESSAGE_BLK_OFFSET;
75d04ada6cSJoseph Chen #endif
76ea8b124aSJoseph Chen 
77ea8b124aSJoseph Chen 	/*
78*666b2d1fSJoseph Chen 	 * Env variable "reboot_mode" which is added by U-Boot, reading ever time.
79ea8b124aSJoseph Chen 	 */
80ea8b124aSJoseph Chen 	env_reboot_mode = env_get("reboot_mode");
81ea8b124aSJoseph Chen 	if (env_reboot_mode) {
82ea8b124aSJoseph Chen 		if (!strcmp(env_reboot_mode, "recovery-key")) {
83ea8b124aSJoseph Chen 			printf("boot mode: recovery (key)\n");
84ea8b124aSJoseph Chen 			return BOOT_MODE_RECOVERY;
85ea8b124aSJoseph Chen 		} else if (!strcmp(env_reboot_mode, "recovery-usb")) {
86ea8b124aSJoseph Chen 			printf("boot mode: recovery (usb)\n");
87ea8b124aSJoseph Chen 			return BOOT_MODE_RECOVERY;
88ea8b124aSJoseph Chen 		} else if (!strcmp(env_reboot_mode, "recovery")) {
89ea8b124aSJoseph Chen 			printf("boot mode: recovery (env)\n");
90ea8b124aSJoseph Chen 			return BOOT_MODE_RECOVERY;
91ea8b124aSJoseph Chen 		} else if (!strcmp(env_reboot_mode, "fastboot")) {
92ea8b124aSJoseph Chen 			printf("boot mode: fastboot\n");
93ea8b124aSJoseph Chen 			return BOOT_MODE_BOOTLOADER;
94ea8b124aSJoseph Chen 		}
95ea8b124aSJoseph Chen 	}
96ea8b124aSJoseph Chen 
973aaa96e8SJoseph Chen 	/*
98*666b2d1fSJoseph Chen 	 * Android BCB special handle:
99*666b2d1fSJoseph Chen 	 *    Once the Android BCB offset changed, reinitalize "boot_mode[PM]".
1003aaa96e8SJoseph Chen 	 *
1013aaa96e8SJoseph Chen 	 * Background:
102*666b2d1fSJoseph Chen 	 *    1. there are two Android BCB at the 0KB(google) and 16KB(rk)
103*666b2d1fSJoseph Chen 	 *       offset in misc.img
104*666b2d1fSJoseph Chen 	 *    2. Android image: return 0KB offset if image version >= 10,
105*666b2d1fSJoseph Chen 	 *	 otherwise 16KB
106*666b2d1fSJoseph Chen 	 *    3. Not Android image: return 16KB offset, eg: FIT image.
1073aaa96e8SJoseph Chen 	 *
108*666b2d1fSJoseph Chen 	 * To handle the cases of 16KB and 0KB, we reinitial boot_mode[PM] once
109*666b2d1fSJoseph Chen 	 * Android BCB is changed.
110ea8b124aSJoseph Chen 	 *
111*666b2d1fSJoseph Chen 	 * PH and PL is from boot mode register and reading once.
112ea8b124aSJoseph Chen 	 * PM is from misc.img and should be updated if BCB offset is changed.
113ea8b124aSJoseph Chen 	 * Return the boot mode according to priority: PH > PM > PL.
1143aaa96e8SJoseph Chen 	 */
1153aaa96e8SJoseph Chen 	if (bcb_offset != offset) {
116ea8b124aSJoseph Chen 		boot_mode[PM] = -EINVAL;
1173aaa96e8SJoseph Chen 		bcb_offset = offset;
1183aaa96e8SJoseph Chen 	}
119d04ada6cSJoseph Chen 
120ea8b124aSJoseph Chen 	/* directly return if there is already valid mode */
121ea8b124aSJoseph Chen 	if (boot_mode[PH] != -EINVAL)
122ea8b124aSJoseph Chen 		return boot_mode[PH];
123ea8b124aSJoseph Chen 	else if (boot_mode[PM] != -EINVAL)
124ea8b124aSJoseph Chen 		return boot_mode[PM];
125ea8b124aSJoseph Chen 	else if (boot_mode[PL] != -EINVAL)
126ea8b124aSJoseph Chen 		return boot_mode[PL];
127d04ada6cSJoseph Chen 
128d04ada6cSJoseph Chen 	/*
129d04ada6cSJoseph Chen 	 * Boot mode priority
130d04ada6cSJoseph Chen 	 *
131d04ada6cSJoseph Chen 	 * Anyway, we should set download boot mode as the highest priority, so:
132d04ada6cSJoseph Chen 	 * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx.
133d04ada6cSJoseph Chen 	 */
134d04ada6cSJoseph Chen 	reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
135d04ada6cSJoseph Chen 	if (reg_boot_mode == BOOT_LOADER) {
136d04ada6cSJoseph Chen 		printf("boot mode: loader\n");
137ea8b124aSJoseph Chen 		boot_mode[PH] = BOOT_MODE_LOADER;
138d04ada6cSJoseph Chen 		clear_boot_reg = 1;
139d04ada6cSJoseph Chen 	} else if (reg_boot_mode == BOOT_FASTBOOT) {
140d04ada6cSJoseph Chen 		printf("boot mode: bootloader\n");
141ea8b124aSJoseph Chen 		boot_mode[PH] = BOOT_MODE_BOOTLOADER;
142d04ada6cSJoseph Chen 		clear_boot_reg = 1;
143*666b2d1fSJoseph Chen 	} else if (misc_require_recovery(bcb_offset)) {
144d04ada6cSJoseph Chen 		printf("boot mode: recovery (misc)\n");
145ea8b124aSJoseph Chen 		boot_mode[PM] = BOOT_MODE_RECOVERY;
14628f66075SJoseph Chen 	} else {
147d04ada6cSJoseph Chen 		switch (reg_boot_mode) {
148d04ada6cSJoseph Chen 		case BOOT_NORMAL:
149d04ada6cSJoseph Chen 			printf("boot mode: normal\n");
150ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_NORMAL;
151d04ada6cSJoseph Chen 			clear_boot_reg = 1;
152d04ada6cSJoseph Chen 			break;
153d04ada6cSJoseph Chen 		case BOOT_RECOVERY:
154d04ada6cSJoseph Chen 			printf("boot mode: recovery (cmd)\n");
155ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_RECOVERY;
156d04ada6cSJoseph Chen 			clear_boot_reg = 1;
157d04ada6cSJoseph Chen 			break;
158d04ada6cSJoseph Chen 		case BOOT_UMS:
159d04ada6cSJoseph Chen 			printf("boot mode: ums\n");
160ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_UMS;
161d04ada6cSJoseph Chen 			clear_boot_reg = 1;
162d04ada6cSJoseph Chen 			break;
163d04ada6cSJoseph Chen 		case BOOT_CHARGING:
164d04ada6cSJoseph Chen 			printf("boot mode: charging\n");
165ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_CHARGING;
166d04ada6cSJoseph Chen 			clear_boot_reg = 1;
167d04ada6cSJoseph Chen 			break;
168d04ada6cSJoseph Chen 		case BOOT_PANIC:
169d04ada6cSJoseph Chen 			printf("boot mode: panic\n");
170ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_PANIC;
171d04ada6cSJoseph Chen 			break;
172d04ada6cSJoseph Chen 		case BOOT_WATCHDOG:
173d04ada6cSJoseph Chen 			printf("boot mode: watchdog\n");
174ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_WATCHDOG;
175d04ada6cSJoseph Chen 			break;
176d04ada6cSJoseph Chen 		default:
177d04ada6cSJoseph Chen 			printf("boot mode: None\n");
178ea8b124aSJoseph Chen 			boot_mode[PL] = BOOT_MODE_UNDEFINE;
17928f66075SJoseph Chen 		}
180692bb812SAndy Yan 	}
181d04ada6cSJoseph Chen 
182d04ada6cSJoseph Chen 	/*
183d04ada6cSJoseph Chen 	 * We don't clear boot mode reg when its value stands for the reboot
184d04ada6cSJoseph Chen 	 * reason or others(in the future), the kernel will need and clear it.
185d04ada6cSJoseph Chen 	 */
186d04ada6cSJoseph Chen 	if (clear_boot_reg)
187d04ada6cSJoseph Chen 		writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
188d04ada6cSJoseph Chen 
189ea8b124aSJoseph Chen 	if (boot_mode[PH] != -EINVAL)
190ea8b124aSJoseph Chen 		return boot_mode[PH];
191ea8b124aSJoseph Chen 	else if (boot_mode[PM] != -EINVAL)
192ea8b124aSJoseph Chen 		return boot_mode[PM];
193ea8b124aSJoseph Chen 	else
194ea8b124aSJoseph Chen 		return boot_mode[PL];
195692bb812SAndy Yan }
196692bb812SAndy Yan 
197be55ced3SAndy Yan int setup_boot_mode(void)
198be55ced3SAndy Yan {
19926362955SCody Xie 	char env_preboot[256] = {0};
200be55ced3SAndy Yan 
201d04ada6cSJoseph Chen 	switch (rockchip_get_boot_mode()) {
202b7195498SJoseph Chen 	case BOOT_MODE_BOOTLOADER:
203be55ced3SAndy Yan 		printf("enter fastboot!\n");
20426362955SCody Xie #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV)
20526362955SCody Xie 		snprintf(env_preboot, 256,
20626362955SCody Xie 				"setenv preboot; mmc dev %x; fastboot usb 0; ",
20726362955SCody Xie 				CONFIG_FASTBOOT_FLASH_MMC_DEV);
20826362955SCody Xie #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV)
20926362955SCody Xie 		snprintf(env_preboot, 256,
21026362955SCody Xie 				"setenv preboot; fastboot usb 0; ");
21126362955SCody Xie #endif
21226362955SCody Xie 		env_set("preboot", env_preboot);
213be55ced3SAndy Yan 		break;
214b7195498SJoseph Chen 	case BOOT_MODE_UMS:
215be55ced3SAndy Yan 		printf("enter UMS!\n");
216be55ced3SAndy Yan 		env_set("preboot", "setenv preboot; ums mmc 0");
217be55ced3SAndy Yan 		break;
218b7195498SJoseph Chen 	case BOOT_MODE_LOADER:
219be55ced3SAndy Yan 		printf("enter Rockusb!\n");
22036aa04d9SJoseph Chen 		env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom");
221be55ced3SAndy Yan 		break;
222b7195498SJoseph Chen 	case BOOT_MODE_CHARGING:
223885d92d4SJoseph Chen 		printf("enter charging!\n");
224885d92d4SJoseph Chen 		env_set("preboot", "setenv preboot; charge");
225885d92d4SJoseph Chen 		break;
226be55ced3SAndy Yan 	}
227be55ced3SAndy Yan 
228be55ced3SAndy Yan 	return 0;
229be55ced3SAndy Yan }
230