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 lmb *lmb = &plat_bidram.lmb; 123 struct lmb_property *mem_rgn = lmb->memory.region; 124 struct lmb_property *res_rgn = lmb->reserved.region; 125 int rsv_cnt = lmb->reserved.cnt; 126 int i, idx = 0; 127 128 if (!gd || !gd->bd) { 129 BIDRAM_D("Ignore bi dram bank update\n"); 130 return; 131 } 132 133 /* 134 * LBM default init: 135 * lmb->reserved.cnt = 1; 136 * lmb->reserved.region[0].base = 0; 137 * lmb->reserved.region[0].size = 0; 138 * 139 * Here handle that: there is the only one dram bank available. 140 */ 141 if (rsv_cnt == 1 && !res_rgn[0].base && !res_rgn[0].size) { 142 gd->bd->bi_dram[0].start = mem_rgn[0].base; 143 gd->bd->bi_dram[0].size = mem_rgn[0].size; 144 goto done; 145 } 146 147 /* If reserved rgn is not from sdram start */ 148 if (res_rgn[0].base != mem_rgn[0].base) { 149 gd->bd->bi_dram[idx].start = mem_rgn[0].base; 150 gd->bd->bi_dram[idx].size = res_rgn[0].base - 151 gd->bd->bi_dram[idx].start; 152 idx++; 153 } 154 155 /* 156 * Note: If reserved rgn is not from sdram start, idx=1 now, otherwise 0. 157 */ 158 for (i = 0; i < rsv_cnt; i++, idx++) { 159 if (res_rgn[i].base + res_rgn[i].size >= gd->ram_top) 160 goto done; 161 162 gd->bd->bi_dram[idx].start = res_rgn[i].base + res_rgn[i].size; 163 if (i + 1 < rsv_cnt) 164 gd->bd->bi_dram[idx].size = res_rgn[i + 1].base - 165 gd->bd->bi_dram[idx].start; 166 else 167 gd->bd->bi_dram[idx].size = gd->ram_top - 168 gd->bd->bi_dram[idx].start; 169 } 170 done: 171 for (i = 0; i < idx; i++) { 172 BIDRAM_D("gd bi_dram[%d]: start=0x%08lx, end=0x%08lx\n", 173 i, (ulong)gd->bd->bi_dram[i].start, 174 (ulong)gd->bd->bi_dram[i].start + 175 (ulong)gd->bd->bi_dram[i].size); 176 } 177 } 178 179 static int bidram_is_overlap(phys_addr_t base1, phys_size_t size1, 180 phys_addr_t base2, phys_size_t size2) 181 { 182 return ((base1 < (base2 + size2)) && (base2 < (base1 + size1))); 183 } 184 185 struct memblock *bidram_reserved_is_overlap(phys_addr_t base, phys_size_t size) 186 { 187 struct bidram *bidram = &plat_bidram; 188 struct list_head *node; 189 struct memblock *mem; 190 191 if (!bidram_has_init()) 192 return false; 193 194 list_for_each(node, &bidram->reserved_head) { 195 mem = list_entry(node, struct memblock, node); 196 if (bidram_is_overlap(mem->base, mem->size, base, size)) 197 return mem; 198 } 199 200 return NULL; 201 } 202 203 static int bidram_core_reserve(enum memblk_id id, const char *mem_name, 204 phys_addr_t base, phys_size_t size) 205 { 206 struct bidram *bidram = &plat_bidram; 207 struct memblk_attr attr; 208 struct memblock *mem; 209 struct list_head *node; 210 const char *name; 211 int ret; 212 213 if (!bidram_has_init()) 214 return -ENOSYS; 215 216 if (id == MEMBLK_ID_BY_NAME) { 217 if (!mem_name) { 218 BIDRAM_E("NULL name for reserve bidram\n"); 219 return -EINVAL; 220 } else { 221 name = mem_name; 222 } 223 } else { 224 if (id > MEMBLK_ID_UNK && id < MEMBLK_ID_MAX) { 225 attr = mem_attr[id]; 226 name = attr.name; 227 } else { 228 BIDRAM_E("Unsupport memblk id %d for reserve bidram\n", id); 229 return -EINVAL; 230 } 231 } 232 233 if (!name) { 234 BIDRAM_E("NULL name for reserved bidram\n"); 235 return -EINVAL; 236 } 237 238 if (!size) 239 return 0; 240 241 /* Check overlap */ 242 list_for_each(node, &bidram->reserved_head) { 243 mem = list_entry(node, struct memblock, node); 244 BIDRAM_D("Has reserved: %s 0x%08lx - 0x%08lx\n", 245 mem->attr.name, (ulong)mem->base, 246 (ulong)(mem->base + mem->size)); 247 if (!strcmp(mem->attr.name, name)) { 248 BIDRAM_E("Failed to double reserve for existence \"%s\"\n", name); 249 return -EEXIST; 250 } else if (bidram_is_overlap(mem->base, mem->size, base, size)) { 251 BIDRAM_D("\"%s\" (0x%08lx - 0x%08lx) reserve is " 252 "overlap with existence \"%s\" (0x%08lx - " 253 "0x%08lx)\n", 254 name, (ulong)base, (ulong)(base + size), mem->attr.name, 255 (ulong)mem->base, (ulong)(mem->base + mem->size)); 256 } 257 } 258 259 BIDRAM_D("Reserve: \"%s\" 0x%08lx - 0x%08lx\n", 260 name, (ulong)base, (ulong)(base + size)); 261 262 ret = lmb_reserve(&bidram->lmb, base, size); 263 if (ret >= 0) { 264 mem = malloc(sizeof(*mem)); 265 if (!mem) { 266 BIDRAM_E("No memory for \"%s\" reserve bidram\n", name); 267 return -ENOMEM; 268 } 269 270 #ifdef CONFIG_SYSMEM 271 /* Sync to sysmem */ 272 if (sysmem_has_init()) { 273 void *paddr; 274 275 if (id == MEMBLK_ID_BY_NAME) 276 paddr = sysmem_alloc_base_by_name(name, base, size); 277 else 278 paddr = sysmem_alloc_base(id, base, size); 279 if (!paddr) { 280 BIDRAM_E("Sync \"%s\" to sysmem failed\n", name); 281 return -ENOMEM; 282 } 283 } 284 #endif 285 mem->base = base; 286 mem->size = size; 287 if (id == MEMBLK_ID_BY_NAME) { 288 mem->attr.name = name; 289 mem->attr.flags = 0; 290 } else { 291 mem->attr = attr; 292 } 293 list_add_tail(&mem->node, &bidram->reserved_head); 294 } else { 295 BIDRAM_E("Failed to reserve \"%s\" 0x%08lx - 0x%08lx\n", 296 name, (ulong)base, (ulong)(base + size)); 297 return -EINVAL; 298 } 299 300 return 0; 301 } 302 303 int bidram_reserve(enum memblk_id id, phys_addr_t base, phys_size_t size) 304 { 305 int ret; 306 307 ret = bidram_core_reserve(id, NULL, base, size); 308 if (!ret) 309 bidram_gen_gd_bi_dram(); 310 else 311 bidram_dump(); 312 313 return ret; 314 } 315 316 int bidram_reserve_by_name(const char *name, 317 phys_addr_t base, phys_size_t size) 318 { 319 int ret; 320 321 ret = bidram_core_reserve(MEMBLK_ID_BY_NAME, name, base, size); 322 if (!ret) 323 bidram_gen_gd_bi_dram(); 324 else 325 bidram_dump(); 326 327 return ret; 328 } 329 330 int bidram_initr(void) 331 { 332 return !bidram_get_ram_size(); 333 } 334 335 phys_size_t bidram_get_ram_size(void) 336 { 337 struct bidram *bidram = &plat_bidram; 338 struct memblock bad[MAX_BAD_MEMBLK]; 339 struct memblock *list; 340 phys_size_t ram_addr_end = CONFIG_SYS_SDRAM_BASE; 341 phys_addr_t end_addr; 342 parse_fn_t parse_fn; 343 int i, count, ret; 344 int bad_cnt = 0; 345 char bad_name[12]; 346 347 parse_fn = board_bidram_parse_fn(); 348 if (!parse_fn) { 349 BIDRAM_E("Can't find dram parse fn\n"); 350 return 0; 351 } 352 353 list = parse_fn(&count); 354 if (!list) { 355 BIDRAM_E("Can't get dram banks\n"); 356 return 0; 357 } 358 359 if (count > CONFIG_NR_DRAM_BANKS) { 360 BIDRAM_E("Too many dram banks, %d is over max: %d\n", 361 count, CONFIG_NR_DRAM_BANKS); 362 return 0; 363 } 364 365 /* Initial plat_bidram */ 366 lmb_init(&bidram->lmb); 367 INIT_LIST_HEAD(&bidram->reserved_head); 368 bidram->has_init = true; 369 370 /* Initial memory pool */ 371 for (i = 0; i < count; i++) { 372 BIDRAM_D("Add bank[%d] start=0x%08lx, end=0x%08lx\n", 373 i, (ulong)list[i].base, 374 (ulong)list[i].base + (ulong)list[i].size); 375 376 if (!list[i].size) 377 continue; 378 379 /* We assume the last block gives the ram addr end */ 380 if (i == count - 1) { 381 ram_addr_end = list[i].base + list[i].size; 382 ret = bidram_add(CONFIG_SYS_SDRAM_BASE, 383 ram_addr_end - CONFIG_SYS_SDRAM_BASE); 384 if (ret) { 385 BIDRAM_E("Failed to add bidram from bi_dram[%d]\n", i); 386 return 0; 387 } 388 } 389 390 /* This is a bad dram bank? record it */ 391 if (i > 0) { 392 end_addr = list[i - 1].base + list[i - 1].size; 393 394 if (list[i].base != end_addr) { 395 snprintf(bad_name, 12, "%s%d", "BAD_RAM.", i - 1); 396 bad[bad_cnt].attr.name = strdup(bad_name); 397 bad[bad_cnt].base = end_addr; 398 bad[bad_cnt].size = list[i].base - end_addr; 399 bad_cnt++; 400 if (bad_cnt > MAX_BAD_MEMBLK) { 401 BIDRAM_E("Too many bad memory blocks\n"); 402 return 0; 403 } 404 } 405 } 406 } 407 408 /* Reserve bad dram bank after bidram_add(), treat as reserved region */ 409 for (i = 0; i < bad_cnt; i++) { 410 if (gd->flags & GD_FLG_RELOC) 411 BIDRAM_R("Bad memblk%d: 0x%08lx - 0x%08lx\n", 412 i, (ulong)bad[i].base, 413 (ulong)bad[i].base + (ulong)bad[i].size); 414 415 ret = bidram_reserve_by_name(bad[i].attr.name, 416 bad[i].base, bad[i].size); 417 if (ret) { 418 BIDRAM_E("Failed to add bad memblk[%d]\n", i); 419 return 0; 420 } 421 } 422 423 /* Reserved for board */ 424 ret = board_bidram_reserve(bidram); 425 if (ret) { 426 BIDRAM_E("Failed to reserve bidram for board\n"); 427 return 0; 428 } 429 430 BIDRAM_D("DRAM size: 0x%08lx\n", 431 (ulong)ram_addr_end - CONFIG_SYS_SDRAM_BASE); 432 433 #ifdef DEBUG 434 bidram_dump(); 435 #endif 436 437 return (ram_addr_end - CONFIG_SYS_SDRAM_BASE); 438 } 439 440 __weak parse_fn_t board_bidram_parse_fn(void) 441 { 442 /* please define platform specific board_bidram_parse_fn() */ 443 return NULL; 444 } 445 446 __weak int board_bidram_reserve(struct bidram *bidram) 447 { 448 /* please define platform specific board_bidram_reserve() */ 449 return 0; 450 } 451 452 static int do_dump_bidram(cmd_tbl_t *cmdtp, int flag, 453 int argc, char *const argv[]) 454 { 455 bidram_dump(); 456 return 0; 457 } 458 459 U_BOOT_CMD( 460 dump_bidram, 1, 1, do_dump_bidram, 461 "Dump bidram layout", 462 "" 463 ); 464