xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/boot_mode.c (revision ea8b124a0eccc3e61b57f2bad26c4dcfb5ec9683)
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 /*
16  * Generally, we have 3 ways to get reboot mode:
17  *
18  * 1. from bootloader_message which is defined in MISC partition;
19  * 2. from CONFIG_ROCKCHIP_BOOT_MODE_REG which supports "reboot xxx" commands;
20  * 3. from env "reboot_mode" which is added by U-Boot code(currently only when
21  *    recovery key pressed);
22  *
23  * 1st and 2nd cases are static determined at system start and we check it once,
24  * while 3th case is dynamically added by U-Boot code, so we have to check it
25  * everytime.
26  *
27  * Recovery mode from:
28  *	- MISC partition;
29  *	- "reboot recovery" command;
30  *	- recovery key pressed without usb attach;
31  */
32 
33 enum {
34 	PH = 0,	/* P: Priority, H: high, M: middle, L: low*/
35 	PM,
36 	PL,
37 };
38 
39 int rockchip_get_boot_mode(void)
40 {
41 	struct bootloader_message *bmsg = NULL;
42 	struct blk_desc *dev_desc;
43 	disk_partition_t part_info;
44 	uint32_t reg_boot_mode;
45 	char *env_reboot_mode;
46 	static int boot_mode[] =		/* static */
47 		{ -EINVAL, -EINVAL, -EINVAL };
48 	static int bcb_offset = -EINVAL;	/* static */
49 	int clear_boot_reg = 0;
50 	int ret, cnt;
51 #ifdef CONFIG_ANDROID_BOOT_IMAGE
52 	u32 offset = android_bcb_msg_sector_offset();
53 #else
54 	u32 offset = BCB_MESSAGE_BLK_OFFSET;
55 #endif
56 
57 	/*
58 	 * Here, we mainly check for:
59 	 * In rockusb_download(), that recovery key is pressed without
60 	 * USB attach will do env_set("reboot_mode", "recovery");
61 	 */
62 	env_reboot_mode = env_get("reboot_mode");
63 	if (env_reboot_mode) {
64 		if (!strcmp(env_reboot_mode, "recovery-key")) {
65 			printf("boot mode: recovery (key)\n");
66 			return BOOT_MODE_RECOVERY;
67 		} else if (!strcmp(env_reboot_mode, "recovery-usb")) {
68 			printf("boot mode: recovery (usb)\n");
69 			return BOOT_MODE_RECOVERY;
70 		} else if (!strcmp(env_reboot_mode, "recovery")) {
71 			printf("boot mode: recovery (env)\n");
72 			return BOOT_MODE_RECOVERY;
73 		} else if (!strcmp(env_reboot_mode, "fastboot")) {
74 			printf("boot mode: fastboot\n");
75 			return BOOT_MODE_BOOTLOADER;
76 		}
77 	}
78 
79 	/*
80 	 * Special handle:
81 	 *    Once the BCB offset changes, reinitalize "boot_mode".
82 	 *
83 	 * Background:
84 	 *    1. there are two Android BCB at the 0x00 and 0x20 offset in
85 	 *       misc.img to compatible legacy(0x20) SDK.
86 	 *    2. android_bcb_msg_sector_offset() is for android image:
87 	 *       return 0x20 if image version < 10, otherwise 0x00.
88 	 *    3. If not android image, BCB at 0x20 is the valid one.
89 	 *
90 	 * U-Boot can support booting both FIT & Android image, if FIT
91 	 * boot flow enters here early than Android, the "boot_mode" is
92 	 * set as BOOT_MODE_RECOVERY according to BCB at 0x20 offset.
93 	 * After that, this function always return static variable "boot_mode"
94 	 * as BOOT_MODE_RECOVERY even android(>=10) boot flow enter here.
95 	 *
96 	 * PH and PL is from boot mode register, reading once.
97 	 * PM is from misc.img and should be updated if BCB offset is changed.
98 	 * Return the boot mode according to priority: PH > PM > PL.
99 	 */
100 	if (bcb_offset != offset) {
101 		boot_mode[PM] = -EINVAL;
102 		bcb_offset = offset;
103 	}
104 
105 	/* directly return if there is already valid mode */
106 	if (boot_mode[PH] != -EINVAL)
107 		return boot_mode[PH];
108 	else if (boot_mode[PM] != -EINVAL)
109 		return boot_mode[PM];
110 	else if (boot_mode[PL] != -EINVAL)
111 		return boot_mode[PL];
112 
113 	dev_desc = rockchip_get_bootdev();
114 	if (!dev_desc) {
115 		printf("dev_desc is NULL!\n");
116 		return -ENODEV;
117 	}
118 
119 	ret = part_get_info_by_name(dev_desc, PART_MISC, &part_info);
120 	if (ret < 0) {
121 		printf("No misc partition\n");
122 		goto fallback;
123 	}
124 
125 	cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz);
126 	bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz);
127 	ret = blk_dread(dev_desc, part_info.start + bcb_offset, cnt, bmsg);
128 	if (ret != cnt) {
129 		free(bmsg);
130 		return -EIO;
131 	}
132 
133 fallback:
134 	/*
135 	 * Boot mode priority
136 	 *
137 	 * Anyway, we should set download boot mode as the highest priority, so:
138 	 *
139 	 * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx.
140 	 */
141 	reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
142 	if (reg_boot_mode == BOOT_LOADER) {
143 		printf("boot mode: loader\n");
144 		boot_mode[PH] = BOOT_MODE_LOADER;
145 		clear_boot_reg = 1;
146 	} else if (reg_boot_mode == BOOT_FASTBOOT) {
147 		printf("boot mode: bootloader\n");
148 		boot_mode[PH] = BOOT_MODE_BOOTLOADER;
149 		clear_boot_reg = 1;
150 	} else if (bmsg && !strcmp(bmsg->command, "boot-recovery")) {
151 		printf("boot mode: recovery (misc)\n");
152 		boot_mode[PM] = BOOT_MODE_RECOVERY;
153 	} else {
154 		switch (reg_boot_mode) {
155 		case BOOT_NORMAL:
156 			printf("boot mode: normal\n");
157 			boot_mode[PL] = BOOT_MODE_NORMAL;
158 			clear_boot_reg = 1;
159 			break;
160 		case BOOT_RECOVERY:
161 			printf("boot mode: recovery (cmd)\n");
162 			boot_mode[PL] = BOOT_MODE_RECOVERY;
163 			clear_boot_reg = 1;
164 			break;
165 		case BOOT_UMS:
166 			printf("boot mode: ums\n");
167 			boot_mode[PL] = BOOT_MODE_UMS;
168 			clear_boot_reg = 1;
169 			break;
170 		case BOOT_CHARGING:
171 			printf("boot mode: charging\n");
172 			boot_mode[PL] = BOOT_MODE_CHARGING;
173 			clear_boot_reg = 1;
174 			break;
175 		case BOOT_PANIC:
176 			printf("boot mode: panic\n");
177 			boot_mode[PL] = BOOT_MODE_PANIC;
178 			break;
179 		case BOOT_WATCHDOG:
180 			printf("boot mode: watchdog\n");
181 			boot_mode[PL] = BOOT_MODE_WATCHDOG;
182 			break;
183 		default:
184 			printf("boot mode: None\n");
185 			boot_mode[PL] = BOOT_MODE_UNDEFINE;
186 		}
187 	}
188 
189 	/*
190 	 * We don't clear boot mode reg when its value stands for the reboot
191 	 * reason or others(in the future), the kernel will need and clear it.
192 	 */
193 	if (clear_boot_reg)
194 		writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
195 
196 	if (boot_mode[PH] != -EINVAL)
197 		return boot_mode[PH];
198 	else if (boot_mode[PM] != -EINVAL)
199 		return boot_mode[PM];
200 	else
201 		return boot_mode[PL];
202 }
203 
204 int setup_boot_mode(void)
205 {
206 	char env_preboot[256] = {0};
207 
208 	switch (rockchip_get_boot_mode()) {
209 	case BOOT_MODE_BOOTLOADER:
210 		printf("enter fastboot!\n");
211 #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV)
212 		snprintf(env_preboot, 256,
213 				"setenv preboot; mmc dev %x; fastboot usb 0; ",
214 				CONFIG_FASTBOOT_FLASH_MMC_DEV);
215 #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV)
216 		snprintf(env_preboot, 256,
217 				"setenv preboot; fastboot usb 0; ");
218 #endif
219 		env_set("preboot", env_preboot);
220 		break;
221 	case BOOT_MODE_UMS:
222 		printf("enter UMS!\n");
223 		env_set("preboot", "setenv preboot; ums mmc 0");
224 		break;
225 	case BOOT_MODE_LOADER:
226 		printf("enter Rockusb!\n");
227 		env_set("preboot", "setenv preboot; rockusb 0 ${devtype} ${devnum}; rbrom");
228 		break;
229 	case BOOT_MODE_CHARGING:
230 		printf("enter charging!\n");
231 		env_set("preboot", "setenv preboot; charge");
232 		break;
233 	}
234 
235 	return 0;
236 }
237