1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2019 Stephan Gerhold <stephan@gerhold.net>
4  */
5 #include <common.h>
6 #include <env.h>
7 #include <init.h>
8 #include <log.h>
9 #include <stdlib.h>
10 #include <asm/global_data.h>
11 #include <asm/setup.h>
12 #include <asm/system.h>
13 
14 DECLARE_GLOBAL_DATA_PTR;
15 
16 /* Parse atags provided by Samsung bootloader to get available memory */
17 static ulong fw_mach __section(".data");
18 static ulong fw_atags __section(".data");
19 
20 static const struct tag *fw_atags_copy;
21 static uint fw_atags_size;
22 
save_boot_params(ulong r0,ulong r1,ulong r2,ulong r3)23 void save_boot_params(ulong r0, ulong r1, ulong r2, ulong r3)
24 {
25 	fw_mach = r1;
26 	fw_atags = r2;
27 	save_boot_params_ret();
28 }
29 
fw_atags_get(void)30 static const struct tag *fw_atags_get(void)
31 {
32 	const struct tag *tags = (const struct tag *)fw_atags;
33 
34 	if (tags->hdr.tag != ATAG_CORE) {
35 		log_err("Invalid atags: tag 0x%x at %p\n", tags->hdr.tag, tags);
36 		return NULL;
37 	}
38 
39 	return tags;
40 }
41 
dram_init(void)42 int dram_init(void)
43 {
44 	const struct tag *t, *tags = fw_atags_get();
45 
46 	if (!tags)
47 		return -EINVAL;
48 
49 	for_each_tag(t, tags) {
50 		if (t->hdr.tag != ATAG_MEM)
51 			continue;
52 
53 		debug("Memory: %#x-%#x (size %#x)\n", t->u.mem.start,
54 		      t->u.mem.start + t->u.mem.size, t->u.mem.size);
55 		gd->ram_size += t->u.mem.size;
56 	}
57 	return 0;
58 }
59 
dram_init_banksize(void)60 int dram_init_banksize(void)
61 {
62 	const struct tag *t, *tags = fw_atags_get();
63 	unsigned int bank = 0;
64 
65 	if (!tags)
66 		return -EINVAL;
67 
68 	for_each_tag(t, tags) {
69 		if (t->hdr.tag != ATAG_MEM)
70 			continue;
71 
72 		gd->bd->bi_dram[bank].start = t->u.mem.start;
73 		gd->bd->bi_dram[bank].size = t->u.mem.size;
74 		if (++bank == CONFIG_NR_DRAM_BANKS)
75 			break;
76 	}
77 	return 0;
78 }
79 
board_init(void)80 int board_init(void)
81 {
82 	gd->bd->bi_arch_number = fw_mach;
83 	gd->bd->bi_boot_params = fw_atags;
84 	return 0;
85 }
86 
parse_serial(const struct tag_serialnr * serialnr)87 static void parse_serial(const struct tag_serialnr *serialnr)
88 {
89 	char serial[17];
90 
91 	if (env_get("serial#"))
92 		return;
93 
94 	sprintf(serial, "%08x%08x", serialnr->high, serialnr->low);
95 	env_set("serial#", serial);
96 }
97 
98 /*
99  * The downstream/vendor kernel (provided by Samsung) uses ATAGS for booting.
100  * It also requires an extremely long cmdline provided by the primary bootloader
101  * that is not suitable for booting mainline.
102  *
103  * Since downstream is the only user of ATAGS, we emulate the behavior of the
104  * Samsung bootloader by generating only the initrd atag in U-Boot, and copying
105  * all other ATAGS as-is from the primary bootloader.
106  */
skip_atag(u32 tag)107 static inline bool skip_atag(u32 tag)
108 {
109 	return (tag == ATAG_NONE || tag == ATAG_CORE ||
110 		tag == ATAG_INITRD || tag == ATAG_INITRD2);
111 }
112 
copy_atags(const struct tag * tags)113 static void copy_atags(const struct tag *tags)
114 {
115 	const struct tag *t;
116 	struct tag *copy;
117 
118 	if (!tags)
119 		return;
120 
121 	/* Calculate necessary size for tags we want to copy */
122 	for_each_tag(t, tags) {
123 		if (skip_atag(t->hdr.tag))
124 			continue;
125 
126 		if (t->hdr.tag == ATAG_SERIAL)
127 			parse_serial(&t->u.serialnr);
128 
129 		fw_atags_size += t->hdr.size * sizeof(u32);
130 	}
131 
132 	if (!fw_atags_size)
133 		return;  /* No tags to copy */
134 
135 	copy = malloc(fw_atags_size);
136 	if (!copy)
137 		return;
138 	fw_atags_copy = copy;
139 
140 	/* Copy tags */
141 	for_each_tag(t, tags) {
142 		if (skip_atag(t->hdr.tag))
143 			continue;
144 
145 		memcpy(copy, t, t->hdr.size * sizeof(u32));
146 		copy = tag_next(copy);
147 	}
148 }
149 
misc_init_r(void)150 int misc_init_r(void)
151 {
152 	copy_atags(fw_atags_get());
153 	return 0;
154 }
155 
setup_board_tags(struct tag ** in_params)156 void setup_board_tags(struct tag **in_params)
157 {
158 	if (!fw_atags_copy)
159 		return;
160 
161 	/*
162 	 * fw_atags_copy contains only full "struct tag" (plus data)
163 	 * so copying it bytewise here should be fine.
164 	 */
165 	memcpy(*in_params, fw_atags_copy, fw_atags_size);
166 	*(u8 **)in_params += fw_atags_size;
167 }
168