Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | // SPDX-License-Identifier: GPL-2.0+ /* * Memory layout parsing for Qualcomm. */ #define LOG_CATEGORY LOGC_BOARD #define pr_fmt(fmt) "QCOM-DRAM: " fmt #include <asm-generic/unaligned.h> #include <dm.h> #include <log.h> #include <sort.h> #include <soc/qcom/smem.h> #define SMEM_USABLE_RAM_PARTITION_TABLE 402 #define RAM_PART_NAME_LENGTH 16 #define RAM_NUM_PART_ENTRIES 32 #define CATEGORY_SDRAM 0x0E #define TYPE_SYSMEM 0x01 static struct { phys_addr_t start; phys_size_t size; } prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 }; struct smem_ram_ptable_hdr { u32 magic[2]; u32 version; u32 reserved; u32 len; } __packed; struct smem_ram_ptn { char name[RAM_PART_NAME_LENGTH]; u64 start; u64 size; u32 attr; u32 category; u32 domain; u32 type; u32 num_partitions; u32 reserved[3]; u32 reserved2[2]; /* The struct grew by 8 bytes at some point */ } __packed; struct smem_ram_ptable { struct smem_ram_ptable_hdr hdr; u32 reserved; /* Added for 8 bytes alignment of header */ struct smem_ram_ptn parts[RAM_NUM_PART_ENTRIES]; } __packed; int dram_init(void) { /* * gd->ram_base / ram_size have been setup already * in qcom_parse_memory(). */ return 0; } static int ddr_bank_cmp(const void *v1, const void *v2) { const struct { phys_addr_t start; phys_size_t size; } *res1 = v1, *res2 = v2; if (!res1->size) return 1; if (!res2->size) return -1; return (res1->start >> 24) - (res2->start >> 24); } /* This has to be done post-relocation since gd->bd isn't preserved */ static void qcom_configure_bi_dram(void) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start; gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size; debug("Bank[%d]: start = %#011llx, size = %#011llx\n", i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); if (!prevbl_ddr_banks[i].size) break; } } int dram_init_banksize(void) { qcom_configure_bi_dram(); return 0; } /* Parse memory map from SMEM, return the number of entries */ static int qcom_parse_memory_smem(phys_addr_t *ram_end) { size_t size; int i, j = 0, ret; struct smem_ram_ptable *ram_ptable; struct smem_ram_ptn *p; ret = qcom_smem_init(); if (ret) { debug("Failed to initialize SMEM: %d.\n", ret); return ret; } ram_ptable = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_USABLE_RAM_PARTITION_TABLE, &size); if (!ram_ptable) { debug("Failed to find SMEM partition.\n"); return -ENODEV; } /* Check validy of RAM */ for (i = 0; i < RAM_NUM_PART_ENTRIES && j < CONFIG_NR_DRAM_BANKS; i++) { p = &ram_ptable->parts[i]; if (p->category != CATEGORY_SDRAM || p->type != TYPE_SYSMEM) continue; if (!p->size && !p->start) break; prevbl_ddr_banks[j].start = p->start; prevbl_ddr_banks[j].size = p->size; *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); j++; } if (j == CONFIG_NR_DRAM_BANKS) log_err("SMEM: More than CONFIG_NR_DRAM_BANKS (%u) entries!", CONFIG_NR_DRAM_BANKS); return j; } static void qcom_parse_memory_dt(const fdt64_t *memory, int banks, phys_addr_t *ram_end) { int i, j; if (banks > CONFIG_NR_DRAM_BANKS) log_err("Provided more memory banks than we can handle\n"); for (i = 0, j = 0; i < banks * 2; i += 2, j++) { prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]); prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]); /* SM8650 boards sometimes have empty regions! */ if (!prevbl_ddr_banks[j].size) { j--; continue; } *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); } } /* * Parse the memory layout from FDT or SMEM: * * If using an internal FDT (where the memory map must have * been written by hand) then we prefer using the layout from * there. This allows overriding SMEM. * * If using an external FDT (coming from ABL), we prefer SMEM * since it is likely to be more accurate / simple, especially * on newer platforms. * * If SMEM parsing fails, we always try to fall back to FDT. * */ void qcom_parse_memory(bool fdt_is_internal) { ofnode node; const fdt64_t *memory; int memsize; phys_addr_t ram_end = 0; int banks; node = ofnode_path("/memory"); if (!ofnode_valid(node)) { log_err("No memory node found in device tree!\n"); return; } memory = ofnode_read_prop(node, "reg", &memsize); if (!memory) { log_err("No memory configuration was provided by the previous bootloader!\n"); return; } banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS); if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2) log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS); qcom_parse_memory_dt(memory, banks, &ram_end); /* * If using an internal FDT but the memory node is empty * then fall back to SMEM. */ if (!prevbl_ddr_banks[0].size && fdt_is_internal) { banks = qcom_parse_memory_smem(&ram_end); if (banks < 0) panic("Couldn't find a valid memory map!\n"); } /* Sort our RAM banks -_- */ qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp); gd->ram_base = prevbl_ddr_banks[0].start; gd->ram_size = ram_end - gd->ram_base; debug("%d banks, ram_base = %#011lx, ram_size = %#011llx, ram_end = %#011llx\n", banks, gd->ram_base, gd->ram_size, ram_end); } |