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