1 /* 2 * Copyright (C) 2012-2015 Panasonic Corporation 3 * Copyright (C) 2015-2017 Socionext Inc. 4 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <fdtdec.h> 11 #include <linux/errno.h> 12 #include <linux/sizes.h> 13 14 #include "init.h" 15 #include "sg-regs.h" 16 #include "soc-info.h" 17 18 DECLARE_GLOBAL_DATA_PTR; 19 20 struct uniphier_memif_data { 21 unsigned int soc_id; 22 unsigned long sparse_ch1_base; 23 int have_ch2; 24 }; 25 26 static const struct uniphier_memif_data uniphier_memif_data[] = { 27 { 28 .soc_id = UNIPHIER_SLD3_ID, 29 .sparse_ch1_base = 0xc0000000, 30 /* 31 * In fact, SLD3 has DRAM ch2, but the memory regions for ch1 32 * and ch2 overlap, and host cannot get access to them at the 33 * same time. Hide the ch2 from U-Boot. 34 */ 35 }, 36 { 37 .soc_id = UNIPHIER_LD4_ID, 38 .sparse_ch1_base = 0xc0000000, 39 }, 40 { 41 .soc_id = UNIPHIER_PRO4_ID, 42 .sparse_ch1_base = 0xa0000000, 43 }, 44 { 45 .soc_id = UNIPHIER_SLD8_ID, 46 .sparse_ch1_base = 0xc0000000, 47 }, 48 { 49 .soc_id = UNIPHIER_PRO5_ID, 50 .sparse_ch1_base = 0xc0000000, 51 }, 52 { 53 .soc_id = UNIPHIER_PXS2_ID, 54 .sparse_ch1_base = 0xc0000000, 55 .have_ch2 = 1, 56 }, 57 { 58 .soc_id = UNIPHIER_LD6B_ID, 59 .sparse_ch1_base = 0xc0000000, 60 .have_ch2 = 1, 61 }, 62 { 63 .soc_id = UNIPHIER_LD11_ID, 64 .sparse_ch1_base = 0xc0000000, 65 }, 66 { 67 .soc_id = UNIPHIER_LD20_ID, 68 .sparse_ch1_base = 0xc0000000, 69 .have_ch2 = 1, 70 }, 71 { 72 .soc_id = UNIPHIER_PXS3_ID, 73 .sparse_ch1_base = 0xc0000000, 74 .have_ch2 = 1, 75 }, 76 }; 77 UNIPHIER_DEFINE_SOCDATA_FUNC(uniphier_get_memif_data, uniphier_memif_data) 78 79 static int uniphier_memconf_decode(struct uniphier_dram_ch *dram_ch) 80 { 81 const struct uniphier_memif_data *data; 82 unsigned long size; 83 u32 val; 84 85 data = uniphier_get_memif_data(); 86 if (!data) { 87 pr_err("unsupported SoC\n"); 88 return -EINVAL; 89 } 90 91 val = readl(SG_MEMCONF); 92 93 /* set up ch0 */ 94 dram_ch[0].base = CONFIG_SYS_SDRAM_BASE; 95 96 switch (val & SG_MEMCONF_CH0_SZ_MASK) { 97 case SG_MEMCONF_CH0_SZ_64M: 98 size = SZ_64M; 99 break; 100 case SG_MEMCONF_CH0_SZ_128M: 101 size = SZ_128M; 102 break; 103 case SG_MEMCONF_CH0_SZ_256M: 104 size = SZ_256M; 105 break; 106 case SG_MEMCONF_CH0_SZ_512M: 107 size = SZ_512M; 108 break; 109 case SG_MEMCONF_CH0_SZ_1G: 110 size = SZ_1G; 111 break; 112 default: 113 pr_err("error: invald value is set to MEMCONF ch0 size\n"); 114 return -EINVAL; 115 } 116 117 if ((val & SG_MEMCONF_CH0_NUM_MASK) == SG_MEMCONF_CH0_NUM_2) 118 size *= 2; 119 120 dram_ch[0].size = size; 121 122 /* set up ch1 */ 123 dram_ch[1].base = dram_ch[0].base + size; 124 125 if (val & SG_MEMCONF_SPARSEMEM) { 126 if (dram_ch[1].base > data->sparse_ch1_base) { 127 pr_warn("Sparse mem is enabled, but ch0 and ch1 overlap\n"); 128 pr_warn("Only ch0 is available\n"); 129 dram_ch[1].base = 0; 130 return 0; 131 } 132 133 dram_ch[1].base = data->sparse_ch1_base; 134 } 135 136 switch (val & SG_MEMCONF_CH1_SZ_MASK) { 137 case SG_MEMCONF_CH1_SZ_64M: 138 size = SZ_64M; 139 break; 140 case SG_MEMCONF_CH1_SZ_128M: 141 size = SZ_128M; 142 break; 143 case SG_MEMCONF_CH1_SZ_256M: 144 size = SZ_256M; 145 break; 146 case SG_MEMCONF_CH1_SZ_512M: 147 size = SZ_512M; 148 break; 149 case SG_MEMCONF_CH1_SZ_1G: 150 size = SZ_1G; 151 break; 152 default: 153 pr_err("error: invald value is set to MEMCONF ch1 size\n"); 154 return -EINVAL; 155 } 156 157 if ((val & SG_MEMCONF_CH1_NUM_MASK) == SG_MEMCONF_CH1_NUM_2) 158 size *= 2; 159 160 dram_ch[1].size = size; 161 162 if (!data->have_ch2) 163 return 0; 164 165 /* set up ch2 */ 166 dram_ch[2].base = dram_ch[1].base + size; 167 168 switch (val & SG_MEMCONF_CH2_SZ_MASK) { 169 case SG_MEMCONF_CH2_SZ_64M: 170 size = SZ_64M; 171 break; 172 case SG_MEMCONF_CH2_SZ_128M: 173 size = SZ_128M; 174 break; 175 case SG_MEMCONF_CH2_SZ_256M: 176 size = SZ_256M; 177 break; 178 case SG_MEMCONF_CH2_SZ_512M: 179 size = SZ_512M; 180 break; 181 case SG_MEMCONF_CH2_SZ_1G: 182 size = SZ_1G; 183 break; 184 default: 185 pr_err("error: invald value is set to MEMCONF ch2 size\n"); 186 return -EINVAL; 187 } 188 189 if ((val & SG_MEMCONF_CH2_NUM_MASK) == SG_MEMCONF_CH2_NUM_2) 190 size *= 2; 191 192 dram_ch[2].size = size; 193 194 return 0; 195 } 196 197 int dram_init(void) 198 { 199 struct uniphier_dram_ch dram_ch[UNIPHIER_MAX_NR_DRAM_CH] = {}; 200 int ret, i; 201 202 gd->ram_size = 0; 203 204 ret = uniphier_memconf_decode(dram_ch); 205 if (ret) 206 return ret; 207 208 for (i = 0; i < ARRAY_SIZE(dram_ch); i++) { 209 210 if (!dram_ch[i].size) 211 break; 212 213 /* 214 * U-Boot relocates itself to the tail of the memory region, 215 * but it does not expect sparse memory. We use the first 216 * contiguous chunk here. 217 */ 218 if (i > 0 && 219 dram_ch[i - 1].base + dram_ch[i - 1].size < dram_ch[i].base) 220 break; 221 222 gd->ram_size += dram_ch[i].size; 223 } 224 225 return 0; 226 } 227 228 void dram_init_banksize(void) 229 { 230 struct uniphier_dram_ch dram_ch[UNIPHIER_MAX_NR_DRAM_CH] = {}; 231 int i; 232 233 uniphier_memconf_decode(dram_ch); 234 235 for (i = 0; i < ARRAY_SIZE(dram_ch); i++) { 236 if (i >= ARRAY_SIZE(gd->bd->bi_dram)) 237 break; 238 239 gd->bd->bi_dram[i].start = dram_ch[i].base; 240 gd->bd->bi_dram[i].size = dram_ch[i].size; 241 } 242 } 243 244 #ifdef CONFIG_OF_BOARD_SETUP 245 /* 246 * The DRAM PHY requires 64 byte scratch area in each DRAM channel 247 * for its dynamic PHY training feature. 248 */ 249 int ft_board_setup(void *fdt, bd_t *bd) 250 { 251 unsigned long rsv_addr; 252 const unsigned long rsv_size = 64; 253 int i, ret; 254 255 if (uniphier_get_soc_id() != UNIPHIER_LD20_ID) 256 return 0; 257 258 for (i = 0; i < ARRAY_SIZE(gd->bd->bi_dram); i++) { 259 rsv_addr = gd->bd->bi_dram[i].start + gd->bd->bi_dram[i].size; 260 rsv_addr -= rsv_size; 261 262 ret = fdt_add_mem_rsv(fdt, rsv_addr, rsv_size); 263 if (ret) 264 return -ENOSPC; 265 266 printf(" Reserved memory region for DRAM PHY training: addr=%lx size=%lx\n", 267 rsv_addr, rsv_size); 268 } 269 270 return 0; 271 } 272 #endif 273