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