blob: 826c002907d69f624c19bff24257b5382a4d61f4 [file] [log] [blame]
Stephan Gerhold3f1d3e42020-01-04 18:45:19 +01001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2019 Stephan Gerhold <stephan@gerhold.net>
4 */
Stephan Gerholdfa307622021-07-07 12:58:55 +02005#include <env.h>
Linus Walleij1ca696c2022-02-20 23:47:01 +01006#include <fdt_support.h>
Simon Glass97589732020-05-10 11:40:02 -06007#include <init.h>
Stephan Gerholdaee9df32021-07-07 12:58:54 +02008#include <log.h>
Stephan Gerholdfa307622021-07-07 12:58:55 +02009#include <stdlib.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060010#include <linux/errno.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060011#include <asm/global_data.h>
Stephan Gerholdaee9df32021-07-07 12:58:54 +020012#include <asm/setup.h>
13#include <asm/system.h>
Stephan Gerhold3f1d3e42020-01-04 18:45:19 +010014
15DECLARE_GLOBAL_DATA_PTR;
16
Stephan Gerholdaee9df32021-07-07 12:58:54 +020017/* Parse atags provided by Samsung bootloader to get available memory */
18static ulong fw_mach __section(".data");
19static ulong fw_atags __section(".data");
20
Stephan Gerholdfa307622021-07-07 12:58:55 +020021static const struct tag *fw_atags_copy;
22static uint fw_atags_size;
23
Stephan Gerholdaee9df32021-07-07 12:58:54 +020024void 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
31static 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
Stephan Gerhold3f1d3e42020-01-04 18:45:19 +010043int dram_init(void)
44{
Stephan Gerholdaee9df32021-07-07 12:58:54 +020045 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
61int 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 }
Stephan Gerhold3f1d3e42020-01-04 18:45:19 +010078 return 0;
79}
80
81int board_init(void)
82{
Stephan Gerholdaee9df32021-07-07 12:58:54 +020083 gd->bd->bi_arch_number = fw_mach;
84 gd->bd->bi_boot_params = fw_atags;
Stephan Gerhold3f1d3e42020-01-04 18:45:19 +010085 return 0;
86}
Stephan Gerholdfa307622021-07-07 12:58:55 +020087
88static 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
Linus Walleij1ca696c2022-02-20 23:47:01 +010099#define SBL_BOARD "board_id="
100#define SBL_LCDTYPE "lcdtype="
101static ulong board_id = 0;
102static ulong lcdtype = 0;
103
104static 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
Stephan Gerholdfa307622021-07-07 12:58:55 +0200126/*
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 */
135static inline bool skip_atag(u32 tag)
136{
137 return (tag == ATAG_NONE || tag == ATAG_CORE ||
138 tag == ATAG_INITRD || tag == ATAG_INITRD2);
139}
140
141static 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
Linus Walleij1ca696c2022-02-20 23:47:01 +0100157 if (t->hdr.tag == ATAG_CMDLINE)
158 parse_cmdline(&t->u.cmdline);
159
Stephan Gerholdfa307622021-07-07 12:58:55 +0200160 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
181int misc_init_r(void)
182{
183 copy_atags(fw_atags_get());
184 return 0;
185}
186
187void 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}
Linus Walleij1ca696c2022-02-20 23:47:01 +0100199
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
206static 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
233static 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
265int 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}