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