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
misc_require_recovery(u32 bcb_offset,int * bcb_recovery_msg)2142b70accSJoseph Chen static int misc_require_recovery(u32 bcb_offset, int *bcb_recovery_msg)
22666b2d1fSJoseph Chen {
23666b2d1fSJoseph Chen struct bootloader_message *bmsg;
24666b2d1fSJoseph Chen struct blk_desc *dev_desc;
25666b2d1fSJoseph Chen disk_partition_t part;
26666b2d1fSJoseph Chen int cnt, recovery = 0;
27666b2d1fSJoseph Chen
28666b2d1fSJoseph Chen dev_desc = rockchip_get_bootdev();
29666b2d1fSJoseph Chen if (!dev_desc) {
30666b2d1fSJoseph Chen printf("dev_desc is NULL!\n");
31666b2d1fSJoseph Chen goto out;
32666b2d1fSJoseph Chen }
33666b2d1fSJoseph Chen
34666b2d1fSJoseph Chen if (part_get_info_by_name(dev_desc, PART_MISC, &part) < 0) {
35666b2d1fSJoseph Chen printf("No misc partition\n");
36666b2d1fSJoseph Chen goto out;
37666b2d1fSJoseph Chen }
38666b2d1fSJoseph Chen
39666b2d1fSJoseph Chen cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), dev_desc->blksz);
40666b2d1fSJoseph Chen bmsg = memalign(ARCH_DMA_MINALIGN, cnt * dev_desc->blksz);
411d2b0919SJason Zhu if (blk_dread(dev_desc, part.start + bcb_offset, cnt, bmsg) != cnt) {
42666b2d1fSJoseph Chen recovery = 0;
431d2b0919SJason Zhu } else {
44666b2d1fSJoseph Chen recovery = !strcmp(bmsg->command, "boot-recovery");
4542b70accSJoseph Chen if (bcb_recovery_msg) {
4660f07e75SJoseph Chen if (!strcmp(bmsg->recovery, "recovery\n--rk_fwupdate\n"))
4742b70accSJoseph Chen *bcb_recovery_msg = BCB_MSG_RECOVERY_RK_FWUPDATE;
48a18cd24eSJoseph Chen else if (!strcmp(bmsg->recovery, "recovery\n--factory_mode=whole") ||
49a18cd24eSJoseph Chen !strcmp(bmsg->recovery, "recovery\n--factory_mode=small"))
5042b70accSJoseph Chen *bcb_recovery_msg = BCB_MSG_RECOVERY_PCBA;
5142b70accSJoseph Chen }
521d2b0919SJason Zhu }
53666b2d1fSJoseph Chen
54666b2d1fSJoseph Chen free(bmsg);
55666b2d1fSJoseph Chen out:
56666b2d1fSJoseph Chen return recovery;
57666b2d1fSJoseph Chen }
58666b2d1fSJoseph Chen
get_bcb_recovery_msg(void)59706ec1d4SJoseph Chen int get_bcb_recovery_msg(void)
60706ec1d4SJoseph Chen {
6142b70accSJoseph Chen int bcb_recovery_msg = BCB_MSG_RECOVERY_NONE;
6242b70accSJoseph Chen #ifdef CONFIG_ANDROID_BOOT_IMAGE
6342b70accSJoseph Chen u32 bcb_offset = android_bcb_msg_sector_offset();
6442b70accSJoseph Chen #else
6542b70accSJoseph Chen u32 bcb_offset = BCB_MESSAGE_BLK_OFFSET;
6642b70accSJoseph Chen #endif
6742b70accSJoseph Chen misc_require_recovery(bcb_offset, &bcb_recovery_msg);
6842b70accSJoseph Chen
69706ec1d4SJoseph Chen return bcb_recovery_msg;
70706ec1d4SJoseph Chen }
71706ec1d4SJoseph Chen
72666b2d1fSJoseph Chen /*
73666b2d1fSJoseph Chen * There are three ways to get reboot-mode:
74666b2d1fSJoseph Chen *
75666b2d1fSJoseph Chen * No1. Android BCB which is defined in misc.img (0KB or 16KB offset)
76666b2d1fSJoseph Chen * No2. CONFIG_ROCKCHIP_BOOT_MODE_REG that supports "reboot xxx" commands
77666b2d1fSJoseph Chen * No3. Env variable "reboot_mode" which is added by U-Boot
78666b2d1fSJoseph Chen *
79666b2d1fSJoseph Chen * Recovery mode from:
80666b2d1fSJoseph Chen * - Android BCB in misc.img
81666b2d1fSJoseph Chen * - "reboot recovery" command
82666b2d1fSJoseph Chen * - recovery key pressed without usb attach
83666b2d1fSJoseph Chen */
rockchip_get_boot_mode(void)84d04ada6cSJoseph Chen int rockchip_get_boot_mode(void)
8515e088eaSJoseph Chen {
86ea8b124aSJoseph Chen static int boot_mode[] = /* static */
87ea8b124aSJoseph Chen { -EINVAL, -EINVAL, -EINVAL };
88ea8b124aSJoseph Chen static int bcb_offset = -EINVAL; /* static */
89666b2d1fSJoseph Chen uint32_t reg_boot_mode;
90666b2d1fSJoseph Chen char *env_reboot_mode;
91d04ada6cSJoseph Chen int clear_boot_reg = 0;
9242b70accSJoseph Chen int recovery_msg = 0;
93d04ada6cSJoseph Chen #ifdef CONFIG_ANDROID_BOOT_IMAGE
943aaa96e8SJoseph Chen u32 offset = android_bcb_msg_sector_offset();
95d04ada6cSJoseph Chen #else
963aaa96e8SJoseph Chen u32 offset = BCB_MESSAGE_BLK_OFFSET;
97d04ada6cSJoseph Chen #endif
98ea8b124aSJoseph Chen
99ea8b124aSJoseph Chen /*
100666b2d1fSJoseph Chen * Env variable "reboot_mode" which is added by U-Boot, reading ever time.
101ea8b124aSJoseph Chen */
102ea8b124aSJoseph Chen env_reboot_mode = env_get("reboot_mode");
103ea8b124aSJoseph Chen if (env_reboot_mode) {
104ea8b124aSJoseph Chen if (!strcmp(env_reboot_mode, "recovery-key")) {
105ea8b124aSJoseph Chen printf("boot mode: recovery (key)\n");
106ea8b124aSJoseph Chen return BOOT_MODE_RECOVERY;
107ea8b124aSJoseph Chen } else if (!strcmp(env_reboot_mode, "recovery-usb")) {
108ea8b124aSJoseph Chen printf("boot mode: recovery (usb)\n");
109ea8b124aSJoseph Chen return BOOT_MODE_RECOVERY;
110ea8b124aSJoseph Chen } else if (!strcmp(env_reboot_mode, "recovery")) {
111ea8b124aSJoseph Chen printf("boot mode: recovery (env)\n");
112ea8b124aSJoseph Chen return BOOT_MODE_RECOVERY;
113ea8b124aSJoseph Chen } else if (!strcmp(env_reboot_mode, "fastboot")) {
114ea8b124aSJoseph Chen printf("boot mode: fastboot\n");
115ea8b124aSJoseph Chen return BOOT_MODE_BOOTLOADER;
1163d2521fcSJoseph Chen } else if (!strcmp(env_reboot_mode, "normal")) {
1173d2521fcSJoseph Chen printf("boot mode: normal(env)\n");
1183d2521fcSJoseph Chen return BOOT_MODE_NORMAL;
119ea8b124aSJoseph Chen }
120ea8b124aSJoseph Chen }
121ea8b124aSJoseph Chen
1223aaa96e8SJoseph Chen /*
123666b2d1fSJoseph Chen * Android BCB special handle:
124666b2d1fSJoseph Chen * Once the Android BCB offset changed, reinitalize "boot_mode[PM]".
1253aaa96e8SJoseph Chen *
1263aaa96e8SJoseph Chen * Background:
127666b2d1fSJoseph Chen * 1. there are two Android BCB at the 0KB(google) and 16KB(rk)
128666b2d1fSJoseph Chen * offset in misc.img
129666b2d1fSJoseph Chen * 2. Android image: return 0KB offset if image version >= 10,
130666b2d1fSJoseph Chen * otherwise 16KB
131666b2d1fSJoseph Chen * 3. Not Android image: return 16KB offset, eg: FIT image.
1323aaa96e8SJoseph Chen *
133666b2d1fSJoseph Chen * To handle the cases of 16KB and 0KB, we reinitial boot_mode[PM] once
134666b2d1fSJoseph Chen * Android BCB is changed.
135ea8b124aSJoseph Chen *
136666b2d1fSJoseph Chen * PH and PL is from boot mode register and reading once.
137ea8b124aSJoseph Chen * PM is from misc.img and should be updated if BCB offset is changed.
138ea8b124aSJoseph Chen * Return the boot mode according to priority: PH > PM > PL.
1393aaa96e8SJoseph Chen */
1403aaa96e8SJoseph Chen if (bcb_offset != offset) {
141ea8b124aSJoseph Chen boot_mode[PM] = -EINVAL;
1423aaa96e8SJoseph Chen bcb_offset = offset;
1433aaa96e8SJoseph Chen }
144d04ada6cSJoseph Chen
145ea8b124aSJoseph Chen /* directly return if there is already valid mode */
146ea8b124aSJoseph Chen if (boot_mode[PH] != -EINVAL)
147ea8b124aSJoseph Chen return boot_mode[PH];
148ea8b124aSJoseph Chen else if (boot_mode[PM] != -EINVAL)
149ea8b124aSJoseph Chen return boot_mode[PM];
150ea8b124aSJoseph Chen else if (boot_mode[PL] != -EINVAL)
151ea8b124aSJoseph Chen return boot_mode[PL];
152d04ada6cSJoseph Chen
153d04ada6cSJoseph Chen /*
154d04ada6cSJoseph Chen * Boot mode priority
155d04ada6cSJoseph Chen *
156d04ada6cSJoseph Chen * Anyway, we should set download boot mode as the highest priority, so:
157d04ada6cSJoseph Chen * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx.
158d04ada6cSJoseph Chen */
159d04ada6cSJoseph Chen reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
160d04ada6cSJoseph Chen if (reg_boot_mode == BOOT_LOADER) {
161d04ada6cSJoseph Chen printf("boot mode: loader\n");
162ea8b124aSJoseph Chen boot_mode[PH] = BOOT_MODE_LOADER;
163d04ada6cSJoseph Chen clear_boot_reg = 1;
1640efe6414STony Xu } else if (reg_boot_mode == BOOT_DFU) {
1650efe6414STony Xu printf("boot mode: dfu\n");
1660efe6414STony Xu boot_mode[PH] = BOOT_MODE_DFU;
1670efe6414STony Xu clear_boot_reg = 1;
168d04ada6cSJoseph Chen } else if (reg_boot_mode == BOOT_FASTBOOT) {
169d04ada6cSJoseph Chen printf("boot mode: bootloader\n");
170ea8b124aSJoseph Chen boot_mode[PH] = BOOT_MODE_BOOTLOADER;
171d04ada6cSJoseph Chen clear_boot_reg = 1;
17242b70accSJoseph Chen } else if (misc_require_recovery(bcb_offset, &recovery_msg)) {
173d04ada6cSJoseph Chen printf("boot mode: recovery (misc)\n");
174ea8b124aSJoseph Chen boot_mode[PM] = BOOT_MODE_RECOVERY;
17528f66075SJoseph Chen } else {
176d04ada6cSJoseph Chen switch (reg_boot_mode) {
177d04ada6cSJoseph Chen case BOOT_NORMAL:
178d04ada6cSJoseph Chen printf("boot mode: normal\n");
179ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_NORMAL;
180d04ada6cSJoseph Chen clear_boot_reg = 1;
181d04ada6cSJoseph Chen break;
182d04ada6cSJoseph Chen case BOOT_RECOVERY:
183d04ada6cSJoseph Chen printf("boot mode: recovery (cmd)\n");
184ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_RECOVERY;
185d04ada6cSJoseph Chen clear_boot_reg = 1;
186d04ada6cSJoseph Chen break;
187d04ada6cSJoseph Chen case BOOT_UMS:
188d04ada6cSJoseph Chen printf("boot mode: ums\n");
189ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_UMS;
190d04ada6cSJoseph Chen clear_boot_reg = 1;
191d04ada6cSJoseph Chen break;
192d04ada6cSJoseph Chen case BOOT_CHARGING:
193d04ada6cSJoseph Chen printf("boot mode: charging\n");
194ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_CHARGING;
195d04ada6cSJoseph Chen clear_boot_reg = 1;
196d04ada6cSJoseph Chen break;
197d04ada6cSJoseph Chen case BOOT_PANIC:
198d04ada6cSJoseph Chen printf("boot mode: panic\n");
199ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_PANIC;
200d04ada6cSJoseph Chen break;
201d04ada6cSJoseph Chen case BOOT_WATCHDOG:
202d04ada6cSJoseph Chen printf("boot mode: watchdog\n");
203ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_WATCHDOG;
204d04ada6cSJoseph Chen break;
205*8af25352SZorro Liu case BOOT_QUIESCENT:
206*8af25352SZorro Liu printf("boot mode: quiescent\n");
207*8af25352SZorro Liu boot_mode[PL] = BOOT_MODE_QUIESCENT;
208*8af25352SZorro Liu break;
209d04ada6cSJoseph Chen default:
210d04ada6cSJoseph Chen printf("boot mode: None\n");
211ea8b124aSJoseph Chen boot_mode[PL] = BOOT_MODE_UNDEFINE;
21228f66075SJoseph Chen }
213692bb812SAndy Yan }
214d04ada6cSJoseph Chen
215d04ada6cSJoseph Chen /*
216d04ada6cSJoseph Chen * We don't clear boot mode reg when its value stands for the reboot
217d04ada6cSJoseph Chen * reason or others(in the future), the kernel will need and clear it.
218d04ada6cSJoseph Chen */
219d04ada6cSJoseph Chen if (clear_boot_reg)
220d04ada6cSJoseph Chen writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
221d04ada6cSJoseph Chen
222ea8b124aSJoseph Chen if (boot_mode[PH] != -EINVAL)
223ea8b124aSJoseph Chen return boot_mode[PH];
224ea8b124aSJoseph Chen else if (boot_mode[PM] != -EINVAL)
225ea8b124aSJoseph Chen return boot_mode[PM];
226ea8b124aSJoseph Chen else
227ea8b124aSJoseph Chen return boot_mode[PL];
228692bb812SAndy Yan }
229692bb812SAndy Yan
setup_boot_mode(void)230be55ced3SAndy Yan int setup_boot_mode(void)
231be55ced3SAndy Yan {
23226362955SCody Xie char env_preboot[256] = {0};
233be55ced3SAndy Yan
234d04ada6cSJoseph Chen switch (rockchip_get_boot_mode()) {
235b7195498SJoseph Chen case BOOT_MODE_BOOTLOADER:
236be55ced3SAndy Yan printf("enter fastboot!\n");
23726362955SCody Xie #if defined(CONFIG_FASTBOOT_FLASH_MMC_DEV)
23826362955SCody Xie snprintf(env_preboot, 256,
23926362955SCody Xie "setenv preboot; mmc dev %x; fastboot usb 0; ",
24026362955SCody Xie CONFIG_FASTBOOT_FLASH_MMC_DEV);
24126362955SCody Xie #elif defined(CONFIG_FASTBOOT_FLASH_NAND_DEV)
24226362955SCody Xie snprintf(env_preboot, 256,
24326362955SCody Xie "setenv preboot; fastboot usb 0; ");
24426362955SCody Xie #endif
24526362955SCody Xie env_set("preboot", env_preboot);
246be55ced3SAndy Yan break;
247b7195498SJoseph Chen case BOOT_MODE_UMS:
248be55ced3SAndy Yan printf("enter UMS!\n");
249be55ced3SAndy Yan env_set("preboot", "setenv preboot; ums mmc 0");
250be55ced3SAndy Yan break;
2510efe6414STony Xu #if defined(CONFIG_CMD_DFU)
2520efe6414STony Xu case BOOT_MODE_DFU:
2530efe6414STony Xu printf("enter DFU!\n");
2540efe6414STony Xu env_set("preboot", "setenv preboot; dfu 0 ${devtype} ${devnum}; rbrom");
2550efe6414STony Xu break;
2560efe6414STony Xu #endif
257b7195498SJoseph Chen case BOOT_MODE_LOADER:
258be55ced3SAndy Yan printf("enter Rockusb!\n");
25950fd2129SJoseph Chen env_set("preboot", "setenv preboot; download");
26050fd2129SJoseph Chen run_command("download", 0);
261be55ced3SAndy Yan break;
262b7195498SJoseph Chen case BOOT_MODE_CHARGING:
263885d92d4SJoseph Chen printf("enter charging!\n");
264885d92d4SJoseph Chen env_set("preboot", "setenv preboot; charge");
265885d92d4SJoseph Chen break;
266be55ced3SAndy Yan }
267be55ced3SAndy Yan
268be55ced3SAndy Yan return 0;
269be55ced3SAndy Yan }
270