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