1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd 4 */ 5 6 #include <common.h> 7 #include <lmb.h> 8 #include <bidram.h> 9 #include <malloc.h> 10 #include <sysmem.h> 11 #include <asm/io.h> 12 #include <asm/arch/rk_atags.h> 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 #define MAX_BAD_MEMBLK 8 17 18 #define BIDRAM_R(fmt, args...) printf(fmt, ##args) 19 #define BIDRAM_I(fmt, args...) printf("Bidram: "fmt, ##args) 20 #define BIDRAM_W(fmt, args...) printf("Bidram Warn: "fmt, ##args) 21 #define BIDRAM_E(fmt, args...) printf("Bidram Error: "fmt, ##args) 22 #define BIDRAM_D(fmt, args...) debug("Bidram Debug: "fmt, ##args) 23 24 struct bidram plat_bidram __section(".data") = { .has_init = false, }; 25 26 __weak void board_bidram_fixup(void) 27 { 28 } 29 30 __weak u64 board_bidram_append_size(void) 31 { 32 return 0; 33 } 34 35 static int bidram_has_init(void) 36 { 37 if (!plat_bidram.has_init) { 38 BIDRAM_E("Framework is not initialized\n"); 39 return 0; 40 } 41 42 return 1; 43 } 44 45 void bidram_dump(void) 46 { 47 struct bidram *bidram = &plat_bidram; 48 struct lmb *lmb = &bidram->lmb; 49 struct memblock *mem; 50 struct list_head *node; 51 ulong memory_size = 0; 52 ulong reserved_size = 0; 53 ulong i; 54 55 if (!bidram_has_init()) 56 return; 57 58 printf("\n\nbidram_dump_all:\n"); 59 60 /* Memory pool */ 61 printf(" --------------------------------------------------------------------\n"); 62 for (i = 0; i < lmb->memory.cnt; i++) { 63 memory_size += lmb->memory.region[i].size; 64 printf(" memory.rgn[%ld].addr = 0x%08lx - 0x%08lx (size: 0x%08lx)\n", i, 65 (ulong)lmb->memory.region[i].base, 66 (ulong)lmb->memory.region[i].base + 67 (ulong)lmb->memory.region[i].size, 68 (ulong)lmb->memory.region[i].size); 69 } 70 printf("\n memory.total = 0x%08lx (%ld MiB. %ld KiB)\n", 71 (ulong)memory_size, 72 SIZE_MB((ulong)memory_size), 73 SIZE_KB((ulong)memory_size)); 74 75 /* Reserved */ 76 i = 0; 77 printf(" --------------------------------------------------------------------\n"); 78 list_for_each(node, &bidram->reserved_head) { 79 mem = list_entry(node, struct memblock, node); 80 reserved_size += mem->size; 81 printf(" reserved.rgn[%ld].name = \"%s\"\n", i, mem->attr.name); 82 printf(" .addr = 0x%08lx - 0x%08lx (size: 0x%08lx)\n", 83 (ulong)mem->base, (ulong)mem->base + (ulong)mem->size, 84 (ulong)mem->size); 85 i++; 86 } 87 printf("\n reserved.total = 0x%08lx (%ld MiB. %ld KiB)\n", 88 (ulong)reserved_size, 89 SIZE_MB((ulong)reserved_size), 90 SIZE_KB((ulong)reserved_size)); 91 92 /* LMB core reserved */ 93 printf(" --------------------------------------------------------------------\n"); 94 reserved_size = 0; 95 for (i = 0; i < lmb->reserved.cnt; i++) { 96 reserved_size += lmb->reserved.region[i].size; 97 printf(" LMB.reserved[%ld].addr = 0x%08lx - 0x%08lx (size: 0x%08lx)\n", i, 98 (ulong)lmb->reserved.region[i].base, 99 (ulong)lmb->reserved.region[i].base + 100 (ulong)lmb->reserved.region[i].size, 101 (ulong)lmb->reserved.region[i].size); 102 } 103 104 printf("\n reserved.core.total = 0x%08lx (%ld MiB. %ld KiB)\n", 105 (ulong)reserved_size, 106 SIZE_MB((ulong)reserved_size), 107 SIZE_KB((ulong)reserved_size)); 108 printf(" --------------------------------------------------------------------\n\n"); 109 } 110 111 static int bidram_add(phys_addr_t base, phys_size_t size) 112 { 113 struct bidram *bidram = &plat_bidram; 114 int ret; 115 116 if (!bidram_has_init()) 117 return -ENOSYS; 118 119 if (!size) 120 return -EINVAL; 121 122 ret = lmb_add(&bidram->lmb, base, size); 123 if (ret < 0) 124 BIDRAM_E("Failed to add bidram at 0x%08lx - 0x%08lx\n", 125 (ulong)base, (ulong)(base + size)); 126 127 return (ret >= 0) ? 0 : ret; 128 } 129 130 void bidram_gen_gd_bi_dram(void) 131 { 132 struct bidram *bidram = &plat_bidram; 133 struct lmb *lmb = &plat_bidram.lmb; 134 struct lmb_property *mem_rgn = lmb->memory.region; 135 struct lmb_property *res_rgn = lmb->reserved.region; 136 int rsv_cnt = lmb->reserved.cnt; 137 int i, idx = 0; 138 139 if (!gd || !gd->bd) { 140 BIDRAM_D("Ignore bi dram bank update\n"); 141 return; 142 } 143 144 /* 145 * LBM default init: 146 * lmb->reserved.cnt = 1; 147 * lmb->reserved.region[0].base = 0; 148 * lmb->reserved.region[0].size = 0; 149 * 150 * Here handle that: there is the only one dram bank available. 151 */ 152 if (rsv_cnt == 1 && !res_rgn[0].base && !res_rgn[0].size) { 153 gd->bd->bi_dram[idx].start = mem_rgn[0].base; 154 gd->bd->bi_dram[idx].size = mem_rgn[0].size; 155 idx++; 156 goto done; 157 } 158 159 /* If reserved rgn is not from sdram start */ 160 if (res_rgn[0].base != mem_rgn[0].base) { 161 gd->bd->bi_dram[idx].start = mem_rgn[0].base; 162 gd->bd->bi_dram[idx].size = res_rgn[0].base - 163 gd->bd->bi_dram[idx].start; 164 idx++; 165 } 166 167 /* 168 * Note: If reserved rgn is not from sdram start, idx=1 now, otherwise 0. 169 */ 170 for (i = 0; i < rsv_cnt; i++, idx++) { 171 if (res_rgn[i].base + res_rgn[i].size >= gd->ram_top) 172 goto done; 173 174 gd->bd->bi_dram[idx].start = res_rgn[i].base + res_rgn[i].size; 175 if (i + 1 < rsv_cnt) 176 gd->bd->bi_dram[idx].size = res_rgn[i + 1].base - 177 gd->bd->bi_dram[idx].start; 178 else 179 gd->bd->bi_dram[idx].size = gd->ram_top - 180 gd->bd->bi_dram[idx].start; 181 } 182 done: 183 /* Append 4GB+ memory blocks and extend ram top */ 184 if (bidram->fixup) { 185 /* extend ram top */ 186 if (gd->ram_top_ext_size) { 187 int pos = idx - 1; 188 ulong top; 189 190 if (gd->bd->bi_dram[pos].start + 191 gd->bd->bi_dram[pos].size == gd->ram_top) { 192 top = gd->bd->bi_dram[pos].start + gd->bd->bi_dram[pos].size; 193 gd->bd->bi_dram[pos].size += gd->ram_top_ext_size; 194 printf("Extend top: 0x%08lx -> 0x%08lx\n", 195 top, top + (ulong)gd->ram_top_ext_size); 196 } 197 } 198 199 /* append 4GB+ */ 200 for (i = 0; i < MEM_RESV_COUNT; i++) { 201 if (!bidram->size_u64[i]) 202 continue; 203 gd->bd->bi_dram[idx].start = bidram->base_u64[i]; 204 gd->bd->bi_dram[idx].size = bidram->size_u64[i]; 205 BIDRAM_D("FIXUP: gd->bi_dram[%d]: start=0x%llx, size=0x%llx\n", 206 idx, bidram->base_u64[i], bidram->size_u64[i]); 207 idx++; 208 } 209 } 210 211 for (i = 0; i < idx; i++) { 212 BIDRAM_D("GEN: gd->bi_dram[%d]: start=0x%llx, end=0x%llx\n", 213 i, (u64)gd->bd->bi_dram[i].start, 214 (u64)gd->bd->bi_dram[i].start + 215 (u64)gd->bd->bi_dram[i].size); 216 } 217 } 218 219 int bidram_fixup(void) 220 { 221 struct bidram *bidram = &plat_bidram; 222 223 bidram->fixup = true; 224 bidram_gen_gd_bi_dram(); 225 226 board_bidram_fixup(); 227 228 return 0; 229 } 230 231 u64 bidram_append_size(void) 232 { 233 struct bidram *bidram = &plat_bidram; 234 u64 size = 0; 235 int i; 236 237 /* 4GB+ */ 238 for (i = 0; i < MEM_RESV_COUNT; i++) 239 size += bidram->size_u64[i]; 240 241 if (gd->ram_top_ext_size) 242 size += gd->ram_top_ext_size; 243 244 size += board_bidram_append_size(); 245 246 return size; 247 } 248 249 static int bidram_is_overlap(phys_addr_t base1, phys_size_t size1, 250 phys_addr_t base2, phys_size_t size2) 251 { 252 return ((base1 < (base2 + size2)) && (base2 < (base1 + size1))); 253 } 254 255 struct memblock *bidram_reserved_is_overlap(phys_addr_t base, phys_size_t size) 256 { 257 struct bidram *bidram = &plat_bidram; 258 struct list_head *node; 259 struct memblock *mem; 260 261 if (!bidram_has_init()) 262 return false; 263 264 list_for_each(node, &bidram->reserved_head) { 265 mem = list_entry(node, struct memblock, node); 266 if (bidram_is_overlap(mem->base, mem->size, base, size)) 267 return mem; 268 } 269 270 return NULL; 271 } 272 273 static int bidram_core_reserve(enum memblk_id id, const char *mem_name, 274 phys_addr_t base, phys_size_t size) 275 { 276 struct bidram *bidram = &plat_bidram; 277 struct memblk_attr attr; 278 struct memblock *mem; 279 struct list_head *node; 280 const char *name; 281 int ret; 282 283 if (!bidram_has_init()) 284 return -ENOSYS; 285 286 if (id == MEM_BY_NAME) { 287 if (!mem_name) { 288 BIDRAM_E("NULL name for reserve bidram\n"); 289 return -EINVAL; 290 } else { 291 name = mem_name; 292 } 293 } else { 294 if (id > MEM_UNK && id < MEM_MAX) { 295 attr = mem_attr[id]; 296 name = attr.name; 297 } else { 298 BIDRAM_E("Unsupport memblk id %d for reserve bidram\n", id); 299 return -EINVAL; 300 } 301 } 302 303 if (!name) { 304 BIDRAM_E("NULL name for reserved bidram\n"); 305 return -EINVAL; 306 } 307 308 if (!size) 309 return 0; 310 311 /* Check overlap */ 312 list_for_each(node, &bidram->reserved_head) { 313 mem = list_entry(node, struct memblock, node); 314 BIDRAM_D("Has reserved: %s 0x%08lx - 0x%08lx\n", 315 mem->attr.name, (ulong)mem->base, 316 (ulong)(mem->base + mem->size)); 317 if (!strcmp(mem->attr.name, name)) { 318 BIDRAM_E("Failed to double reserve for existence \"%s\"\n", name); 319 return -EEXIST; 320 } else if (bidram_is_overlap(mem->base, mem->size, base, size)) { 321 BIDRAM_D("\"%s\" (0x%08lx - 0x%08lx) reserve is " 322 "overlap with existence \"%s\" (0x%08lx - " 323 "0x%08lx)\n", 324 name, (ulong)base, (ulong)(base + size), mem->attr.name, 325 (ulong)mem->base, (ulong)(mem->base + mem->size)); 326 } 327 } 328 329 BIDRAM_D("Reserve: \"%s\" 0x%08lx - 0x%08lx\n", 330 name, (ulong)base, (ulong)(base + size)); 331 332 ret = lmb_reserve(&bidram->lmb, base, size); 333 if (ret >= 0) { 334 mem = malloc(sizeof(*mem)); 335 if (!mem) { 336 BIDRAM_E("No memory for \"%s\" reserve bidram\n", name); 337 return -ENOMEM; 338 } 339 340 #ifdef CONFIG_SYSMEM 341 /* Sync to sysmem */ 342 if (sysmem_has_init()) { 343 void *paddr; 344 345 if (id == MEM_BY_NAME) 346 paddr = sysmem_alloc_base_by_name(name, base, size); 347 else 348 paddr = sysmem_alloc_base(id, base, size); 349 if (!paddr) { 350 BIDRAM_E("Sync \"%s\" to sysmem failed\n", name); 351 return -ENOMEM; 352 } 353 } 354 #endif 355 mem->base = base; 356 mem->size = size; 357 if (id == MEM_BY_NAME) { 358 mem->attr.name = name; 359 mem->attr.flags = 0; 360 } else { 361 mem->attr = attr; 362 } 363 list_add_tail(&mem->node, &bidram->reserved_head); 364 } else { 365 BIDRAM_E("Failed to reserve \"%s\" 0x%08lx - 0x%08lx\n", 366 name, (ulong)base, (ulong)(base + size)); 367 return -EINVAL; 368 } 369 370 return 0; 371 } 372 373 int bidram_reserve(enum memblk_id id, phys_addr_t base, phys_size_t size) 374 { 375 int ret; 376 377 ret = bidram_core_reserve(id, NULL, base, size); 378 if (!ret) 379 bidram_gen_gd_bi_dram(); 380 else 381 bidram_dump(); 382 383 return ret; 384 } 385 386 int bidram_reserve_by_name(const char *name, 387 phys_addr_t base, phys_size_t size) 388 { 389 int ret; 390 391 ret = bidram_core_reserve(MEM_BY_NAME, name, base, size); 392 if (!ret) 393 bidram_gen_gd_bi_dram(); 394 else 395 bidram_dump(); 396 397 return ret; 398 } 399 400 int bidram_initr(void) 401 { 402 return !bidram_get_ram_size(); 403 } 404 405 phys_size_t bidram_get_ram_size(void) 406 { 407 struct bidram *bidram = &plat_bidram; 408 struct memblock bad[MAX_BAD_MEMBLK]; 409 struct memblock *list; 410 phys_size_t ram_addr_end = CONFIG_SYS_SDRAM_BASE; 411 phys_addr_t end_addr; 412 parse_fn_t parse_fn; 413 int i, count, ret; 414 int bad_cnt = 0, n = 0; 415 char bad_name[12]; 416 417 parse_fn = board_bidram_parse_fn(); 418 if (!parse_fn) { 419 BIDRAM_E("Can't find dram parse fn\n"); 420 return 0; 421 } 422 423 list = parse_fn(&count); 424 if (!list) { 425 BIDRAM_E("Can't get dram banks\n"); 426 return 0; 427 } 428 429 if (count > CONFIG_NR_DRAM_BANKS) { 430 BIDRAM_E("Too many dram banks, %d is over max: %d\n", 431 count, CONFIG_NR_DRAM_BANKS); 432 return 0; 433 } 434 435 /* Initial plat_bidram */ 436 lmb_init(&bidram->lmb); 437 INIT_LIST_HEAD(&bidram->reserved_head); 438 bidram->has_init = true; 439 440 /* Initial memory pool */ 441 for (i = 0; i < count; i++) { 442 BIDRAM_D("Add bank[%d] start=0x%08lx, end=0x%08lx\n", 443 i, (ulong)list[i].base, 444 (ulong)list[i].base + (ulong)list[i].size); 445 446 if (!list[i].size) { 447 /* handle 4GB+ */ 448 if (list[i].size_u64 && n < MEM_RESV_COUNT) { 449 bidram->base_u64[n] = list[i].base_u64; 450 bidram->size_u64[n] = list[i].size_u64; 451 n++; 452 } 453 continue; 454 } 455 456 /* We assume the last block gives the ram addr end */ 457 ram_addr_end = list[i].base + list[i].size; 458 459 /* This is a bad dram bank? record it */ 460 if (i > 0) { 461 end_addr = list[i - 1].base + list[i - 1].size; 462 463 if (list[i].base != end_addr) { 464 snprintf(bad_name, 12, "%s%d", "BAD_RAM.", i - 1); 465 bad[bad_cnt].attr.name = strdup(bad_name); 466 bad[bad_cnt].base = end_addr; 467 bad[bad_cnt].size = list[i].base - end_addr; 468 bad_cnt++; 469 if (bad_cnt > MAX_BAD_MEMBLK) { 470 BIDRAM_E("Too many bad memory blocks\n"); 471 return 0; 472 } 473 } 474 } 475 } 476 477 ret = bidram_add(CONFIG_SYS_SDRAM_BASE, 478 ram_addr_end - CONFIG_SYS_SDRAM_BASE); 479 if (ret) { 480 BIDRAM_E("Failed to add bidram from bi_dram[%d]\n", i); 481 return 0; 482 } 483 484 /* Reserve bad dram bank after bidram_add(), treat as reserved region */ 485 for (i = 0; i < bad_cnt; i++) { 486 if (gd->flags & GD_FLG_RELOC) 487 BIDRAM_R("Bad memblk%d: 0x%08lx - 0x%08lx\n", 488 i, (ulong)bad[i].base, 489 (ulong)bad[i].base + (ulong)bad[i].size); 490 491 ret = bidram_reserve_by_name(bad[i].attr.name, 492 bad[i].base, bad[i].size); 493 if (ret) { 494 BIDRAM_E("Failed to add bad memblk[%d]\n", i); 495 return 0; 496 } 497 } 498 499 /* Reserved for board */ 500 ret = board_bidram_reserve(bidram); 501 if (ret) { 502 BIDRAM_E("Failed to reserve bidram for board\n"); 503 return 0; 504 } 505 506 BIDRAM_D("DRAM size: 0x%08lx\n", 507 (ulong)ram_addr_end - CONFIG_SYS_SDRAM_BASE); 508 509 #ifdef DEBUG 510 bidram_dump(); 511 #endif 512 513 return (ram_addr_end - CONFIG_SYS_SDRAM_BASE); 514 } 515 516 __weak parse_fn_t board_bidram_parse_fn(void) 517 { 518 /* please define platform specific board_bidram_parse_fn() */ 519 return NULL; 520 } 521 522 __weak int board_bidram_reserve(struct bidram *bidram) 523 { 524 /* please define platform specific board_bidram_reserve() */ 525 return 0; 526 } 527 528 static int do_bidram_dump(cmd_tbl_t *cmdtp, int flag, 529 int argc, char *const argv[]) 530 { 531 bidram_dump(); 532 return 0; 533 } 534 535 U_BOOT_CMD( 536 bidram_dump, 1, 1, do_bidram_dump, 537 "Dump bidram layout", 538 "" 539 ); 540