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