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 <fdt_support.h>
8 #include <init.h>
9 #include <log.h>
10 #include <stdlib.h>
11 #include <asm/global_data.h>
12 #include <asm/setup.h>
13 #include <asm/system.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
17 /* Parse atags provided by Samsung bootloader to get available memory */
18 static ulong fw_mach __section(".data");
19 static ulong fw_atags __section(".data");
20
21 static const struct tag *fw_atags_copy;
22 static uint fw_atags_size;
23
save_boot_params(ulong r0,ulong r1,ulong r2,ulong r3)24 void save_boot_params(ulong r0, ulong r1, ulong r2, ulong r3)
25 {
26 fw_mach = r1;
27 fw_atags = r2;
28 save_boot_params_ret();
29 }
30
fw_atags_get(void)31 static const struct tag *fw_atags_get(void)
32 {
33 const struct tag *tags = (const struct tag *)fw_atags;
34
35 if (tags->hdr.tag != ATAG_CORE) {
36 log_err("Invalid atags: tag 0x%x at %p\n", tags->hdr.tag, tags);
37 return NULL;
38 }
39
40 return tags;
41 }
42
dram_init(void)43 int dram_init(void)
44 {
45 const struct tag *t, *tags = fw_atags_get();
46
47 if (!tags)
48 return -EINVAL;
49
50 for_each_tag(t, tags) {
51 if (t->hdr.tag != ATAG_MEM)
52 continue;
53
54 debug("Memory: %#x-%#x (size %#x)\n", t->u.mem.start,
55 t->u.mem.start + t->u.mem.size, t->u.mem.size);
56 gd->ram_size += t->u.mem.size;
57 }
58 return 0;
59 }
60
dram_init_banksize(void)61 int dram_init_banksize(void)
62 {
63 const struct tag *t, *tags = fw_atags_get();
64 unsigned int bank = 0;
65
66 if (!tags)
67 return -EINVAL;
68
69 for_each_tag(t, tags) {
70 if (t->hdr.tag != ATAG_MEM)
71 continue;
72
73 gd->bd->bi_dram[bank].start = t->u.mem.start;
74 gd->bd->bi_dram[bank].size = t->u.mem.size;
75 if (++bank == CONFIG_NR_DRAM_BANKS)
76 break;
77 }
78 return 0;
79 }
80
board_init(void)81 int board_init(void)
82 {
83 gd->bd->bi_arch_number = fw_mach;
84 gd->bd->bi_boot_params = fw_atags;
85 return 0;
86 }
87
parse_serial(const struct tag_serialnr * serialnr)88 static void parse_serial(const struct tag_serialnr *serialnr)
89 {
90 char serial[17];
91
92 if (env_get("serial#"))
93 return;
94
95 sprintf(serial, "%08x%08x", serialnr->high, serialnr->low);
96 env_set("serial#", serial);
97 }
98
99 #define SBL_BOARD "board_id="
100 #define SBL_LCDTYPE "lcdtype="
101 static ulong board_id = 0;
102 static ulong lcdtype = 0;
103
parse_cmdline(const struct tag_cmdline * cmdline)104 static void parse_cmdline(const struct tag_cmdline *cmdline)
105 {
106 char *buf;
107
108 /* Export this to sbl_cmdline (secondary boot loader command line) */
109 env_set("sbl_cmdline", cmdline->cmdline);
110
111 buf = strstr(cmdline->cmdline, SBL_BOARD);
112 if (!buf)
113 return;
114 buf += strlen(SBL_BOARD);
115
116 board_id = simple_strtoul(buf, NULL, 10);
117
118 buf = strstr(cmdline->cmdline, SBL_LCDTYPE);
119 if (!buf)
120 return;
121 buf += strlen(SBL_LCDTYPE);
122
123 lcdtype = simple_strtoul(buf, NULL, 10);
124 }
125
126 /*
127 * The downstream/vendor kernel (provided by Samsung) uses ATAGS for booting.
128 * It also requires an extremely long cmdline provided by the primary bootloader
129 * that is not suitable for booting mainline.
130 *
131 * Since downstream is the only user of ATAGS, we emulate the behavior of the
132 * Samsung bootloader by generating only the initrd atag in U-Boot, and copying
133 * all other ATAGS as-is from the primary bootloader.
134 */
skip_atag(u32 tag)135 static inline bool skip_atag(u32 tag)
136 {
137 return (tag == ATAG_NONE || tag == ATAG_CORE ||
138 tag == ATAG_INITRD || tag == ATAG_INITRD2);
139 }
140
copy_atags(const struct tag * tags)141 static void copy_atags(const struct tag *tags)
142 {
143 const struct tag *t;
144 struct tag *copy;
145
146 if (!tags)
147 return;
148
149 /* Calculate necessary size for tags we want to copy */
150 for_each_tag(t, tags) {
151 if (skip_atag(t->hdr.tag))
152 continue;
153
154 if (t->hdr.tag == ATAG_SERIAL)
155 parse_serial(&t->u.serialnr);
156
157 if (t->hdr.tag == ATAG_CMDLINE)
158 parse_cmdline(&t->u.cmdline);
159
160 fw_atags_size += t->hdr.size * sizeof(u32);
161 }
162
163 if (!fw_atags_size)
164 return; /* No tags to copy */
165
166 copy = malloc(fw_atags_size);
167 if (!copy)
168 return;
169 fw_atags_copy = copy;
170
171 /* Copy tags */
172 for_each_tag(t, tags) {
173 if (skip_atag(t->hdr.tag))
174 continue;
175
176 memcpy(copy, t, t->hdr.size * sizeof(u32));
177 copy = tag_next(copy);
178 }
179 }
180
misc_init_r(void)181 int misc_init_r(void)
182 {
183 copy_atags(fw_atags_get());
184 return 0;
185 }
186
setup_board_tags(struct tag ** in_params)187 void setup_board_tags(struct tag **in_params)
188 {
189 if (!fw_atags_copy)
190 return;
191
192 /*
193 * fw_atags_copy contains only full "struct tag" (plus data)
194 * so copying it bytewise here should be fine.
195 */
196 memcpy(*in_params, fw_atags_copy, fw_atags_size);
197 *(u8 **)in_params += fw_atags_size;
198 }
199
200 /* These numbers are unique per product but not across all products */
201 #define SAMSUNG_CODINA_LCD_LMS380KF01 4
202 #define SAMSUNG_CODINA_LCD_S6D27A1 13
203 #define SAMSUNG_SKOMER_LCD_HVA40WV1 10
204 #define SAMSUNG_SKOMER_LCD_NT35512 12
205
codina_patch_display(void * fdt)206 static void codina_patch_display(void *fdt)
207 {
208 int node;
209 int ret;
210
211 node = fdt_path_offset(fdt, "/spi-gpio-0/panel");
212 if (node < 0) {
213 printf("cannot find Codina panel node\n");
214 return;
215 }
216 if (lcdtype == SAMSUNG_CODINA_LCD_LMS380KF01) {
217 ret = fdt_setprop_string(fdt, node, "compatible", "samsung,lms380kf01");
218 if (ret < 0)
219 printf("could not set LCD compatible\n");
220 else
221 printf("updated LCD compatible to LMS380KF01\n");
222 } else if (lcdtype == SAMSUNG_CODINA_LCD_S6D27A1) {
223 ret = fdt_setprop_string(fdt, node, "compatible", "samsung,s6d27a1");
224 if (ret < 0)
225 printf("could not set LCD compatible\n");
226 else
227 printf("updated LCD compatible to S6D27A1\n");
228 } else {
229 printf("unknown LCD type\n");
230 }
231 }
232
skomer_kyle_patch_display(void * fdt)233 static void skomer_kyle_patch_display(void *fdt)
234 {
235 int node;
236 int ret;
237
238 node = fdt_path_offset(fdt, "/soc/mcde/dsi/panel");
239 if (node < 0) {
240 printf("cannot find Skomer/Kyle panel node\n");
241 return;
242 }
243 if (lcdtype == SAMSUNG_SKOMER_LCD_HVA40WV1) {
244 ret = fdt_setprop_string(fdt, node, "compatible", "hydis,hva40wv1");
245 if (ret < 0)
246 printf("could not set LCD compatible\n");
247 else
248 printf("updated LCD compatible to Hydis HVA40WV1\n");
249 } else if (lcdtype == SAMSUNG_SKOMER_LCD_NT35512) {
250 /*
251 * FIXME: This panel is actually a BOE product, but we don't know
252 * the exact product name, so the compatible for the NT35512
253 * is used for the time being. The vendor drivers also call it NT35512.
254 */
255 ret = fdt_setprop_string(fdt, node, "compatible", "novatek,nt35512");
256 if (ret < 0)
257 printf("could not set LCD compatible\n");
258 else
259 printf("updated LCD compatible to Novatek NT35512\n");
260 } else {
261 printf("unknown LCD type\n");
262 }
263 }
264
ft_board_setup(void * fdt,struct bd_info * bd)265 int ft_board_setup(void *fdt, struct bd_info *bd)
266 {
267 const char *str;
268 int node;
269 int ret;
270
271 printf("stemmy patch: DTB at 0x%08lx\n", (ulong)fdt);
272
273 /* Inspect FDT to see what we've got here */
274 ret = fdt_check_header(fdt);
275 if (ret < 0) {
276 printf("invalid DTB\n");
277 return ret;
278 }
279 node = fdt_path_offset(fdt, "/");
280 if (node < 0) {
281 printf("cannot find root node\n");
282 return node;
283 }
284 str = fdt_stringlist_get(fdt, node, "compatible", 0, NULL);
285 if (!str) {
286 printf("could not find board compatible\n");
287 return -1;
288 }
289
290 if (!strcmp(str, "samsung,janice")) {
291 switch(board_id) {
292 case 7:
293 printf("Janice GT-I9070 Board Rev 0.0\n");
294 break;
295 case 8:
296 printf("Janice GT-I9070 Board Rev 0.1\n");
297 break;
298 case 9:
299 printf("Janice GT-I9070 Board Rev 0.2\n");
300 break;
301 case 10:
302 printf("Janice GT-I9070 Board Rev 0.3\n");
303 break;
304 case 11:
305 printf("Janice GT-I9070 Board Rev 0.4\n");
306 break;
307 case 12:
308 printf("Janice GT-I9070 Board Rev 0.5\n");
309 break;
310 case 13:
311 printf("Janice GT-I9070 Board Rev 0.6\n");
312 break;
313 default:
314 break;
315 }
316 } else if (!strcmp(str, "samsung,gavini")) {
317 switch(board_id) {
318 case 7:
319 printf("Gavini GT-I8530 Board Rev 0.0\n");
320 break;
321 case 8:
322 printf("Gavini GT-I8530 Board Rev 0.0A\n");
323 break;
324 case 9:
325 printf("Gavini GT-I8530 Board Rev 0.0B\n");
326 break;
327 case 10:
328 printf("Gavini GT-I8530 Board Rev 0.0A_EMUL\n");
329 break;
330 case 11:
331 printf("Gavini GT-I8530 Board Rev 0.0C\n");
332 break;
333 case 12:
334 printf("Gavini GT-I8530 Board Rev 0.0D\n");
335 break;
336 case 13:
337 printf("Gavini GT-I8530 Board Rev 0.1\n");
338 break;
339 case 14:
340 printf("Gavini GT-I8530 Board Rev 0.3\n");
341 break;
342 default:
343 break;
344 }
345 } else if (!strcmp(str, "samsung,codina")) {
346 switch(board_id) {
347 case 7:
348 printf("Codina GT-I8160 Board Rev 0.0\n");
349 break;
350 case 8:
351 printf("Codina GT-I8160 Board Rev 0.1\n");
352 break;
353 case 9:
354 printf("Codina GT-I8160 Board Rev 0.2\n");
355 break;
356 case 10:
357 printf("Codina GT-I8160 Board Rev 0.3\n");
358 break;
359 case 11:
360 printf("Codina GT-I8160 Board Rev 0.4\n");
361 break;
362 case 12:
363 printf("Codina GT-I8160 Board Rev 0.5\n");
364 break;
365 default:
366 break;
367 }
368 codina_patch_display(fdt);
369 } else if (!strcmp(str, "samsung,codina-tmo")) {
370 switch(board_id) {
371 case 0x101:
372 printf("Codina SGH-T599 Board pre-Rev 0.0\n");
373 break;
374 case 0x102:
375 printf("Codina SGH-T599 Board Rev 0.0\n");
376 break;
377 case 0x103:
378 printf("Codina SGH-T599 Board Rev 0.1\n");
379 break;
380 case 0x104:
381 printf("Codina SGH-T599 Board Rev 0.2\n");
382 break;
383 case 0x105:
384 printf("Codina SGH-T599 Board Rev 0.4\n");
385 break;
386 case 0x106:
387 printf("Codina SGH-T599 Board Rev 0.6\n");
388 break;
389 case 0x107:
390 printf("Codina SGH-T599 Board Rev 0.7\n");
391 break;
392 default:
393 break;
394 }
395 codina_patch_display(fdt);
396 } else if (!strcmp(str, "samsung,golden")) {
397 switch(board_id) {
398 case 0x102:
399 printf("Golden GT-I8190 Board SW bringup\n");
400 break;
401 case 0x103:
402 printf("Golden GT-I8190 Board Rev 0.2\n");
403 break;
404 case 0x104:
405 printf("Golden GT-I8190 Board Rev 0.3\n");
406 break;
407 case 0x105:
408 printf("Golden GT-I8190 Board Rev 0.4\n");
409 break;
410 case 0x106:
411 printf("Golden GT-I8190 Board Rev 0.5\n");
412 break;
413 case 0x107:
414 printf("Golden GT-I8190 Board Rev 0.6\n");
415 break;
416 default:
417 break;
418 }
419 } else if (!strcmp(str, "samsung,skomer")) {
420 switch(board_id) {
421 case 0x101:
422 printf("Skomer GT-S7710 Board Rev 0.0\n");
423 break;
424 case 0x102:
425 printf("Skomer GT-S7710 Board Rev 0.1\n");
426 break;
427 case 0x103:
428 printf("Skomer GT-S7710 Board Rev 0.2\n");
429 break;
430 case 0x104:
431 printf("Skomer GT-S7710 Board Rev 0.3\n");
432 break;
433 case 0x105:
434 printf("Skomer GT-S7710 Board Rev 0.4\n");
435 break;
436 case 0x106:
437 printf("Skomer GT-S7710 Board Rev 0.5\n");
438 break;
439 case 0x107:
440 printf("Skomer GT-S7710 Board Rev 0.6\n");
441 break;
442 case 0x108:
443 printf("Skomer GT-S7710 Board Rev 0.7\n");
444 break;
445 case 0x109:
446 printf("Skomer GT-S7710 Board Rev 0.8\n");
447 break;
448 default:
449 break;
450 }
451 skomer_kyle_patch_display(fdt);
452 } else if (!strcmp(str, "samsung,kyle")) {
453 switch(board_id) {
454 case 0x101:
455 printf("Kyle SGH-I407 Board Rev 0.0\n");
456 break;
457 case 0x102:
458 printf("Kyle SGH-I407 Board Rev 0.1\n");
459 break;
460 case 0x103:
461 printf("Kyle SGH-I407 Board Rev 0.2\n");
462 break;
463 case 0x104:
464 printf("Kyle SGH-I407 Board Rev 0.3\n");
465 break;
466 case 0x105:
467 printf("Kyle SGH-I407 Board Rev 0.4\n");
468 break;
469 case 0x106:
470 printf("Kyle SGH-I407 Board Rev 0.5\n");
471 break;
472 case 0x107:
473 printf("Kyle SGH-I407 Board Rev 0.6\n");
474 break;
475 default:
476 break;
477 }
478 skomer_kyle_patch_display(fdt);
479 }
480
481 return 0;
482 }
483