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