]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
5894ca00 | 2 | /* |
3e9952be MY |
3 | * Copyright (C) 2012-2015 Panasonic Corporation |
4 | * Copyright (C) 2015-2017 Socionext Inc. | |
5 | * Author: Masahiro Yamada <yamada.masahiro@socionext.com> | |
5894ca00 MY |
6 | */ |
7 | ||
691d719d | 8 | #include <init.h> |
0f4ec05b | 9 | #include <linux/errno.h> |
3b7fc3ff | 10 | #include <linux/io.h> |
dd74b945 MY |
11 | #include <linux/kernel.h> |
12 | #include <linux/printk.h> | |
3e9952be | 13 | #include <linux/sizes.h> |
dd74b945 | 14 | #include <asm/global_data.h> |
6717e154 | 15 | #include <asm/u-boot.h> |
cf88affa | 16 | |
34e29f7d | 17 | #include "init.h" |
3e9952be | 18 | #include "sg-regs.h" |
51ea5a06 MY |
19 | #include "soc-info.h" |
20 | ||
cf88affa MY |
21 | DECLARE_GLOBAL_DATA_PTR; |
22 | ||
04cd4e72 MY |
23 | struct uniphier_dram_map { |
24 | unsigned long base; | |
25 | unsigned long size; | |
26 | }; | |
27 | ||
6f47c994 MY |
28 | static int uniphier_memconf_decode(struct uniphier_dram_map *dram_map, |
29 | unsigned long sparse_ch1_base, bool have_ch2) | |
cf88affa | 30 | { |
3e9952be MY |
31 | unsigned long size; |
32 | u32 val; | |
cf88affa | 33 | |
d41b358f | 34 | val = readl(sg_base + SG_MEMCONF); |
5894ca00 | 35 | |
3e9952be | 36 | /* set up ch0 */ |
1f8357c3 | 37 | dram_map[0].base = 0x80000000; |
3e9952be MY |
38 | |
39 | switch (val & SG_MEMCONF_CH0_SZ_MASK) { | |
40 | case SG_MEMCONF_CH0_SZ_64M: | |
41 | size = SZ_64M; | |
42 | break; | |
43 | case SG_MEMCONF_CH0_SZ_128M: | |
44 | size = SZ_128M; | |
45 | break; | |
46 | case SG_MEMCONF_CH0_SZ_256M: | |
47 | size = SZ_256M; | |
48 | break; | |
49 | case SG_MEMCONF_CH0_SZ_512M: | |
50 | size = SZ_512M; | |
51 | break; | |
52 | case SG_MEMCONF_CH0_SZ_1G: | |
53 | size = SZ_1G; | |
54 | break; | |
55 | default: | |
0f5bf09c | 56 | pr_err("error: invalid value is set to MEMCONF ch0 size\n"); |
cf88affa | 57 | return -EINVAL; |
ac2a1030 MY |
58 | } |
59 | ||
3e9952be MY |
60 | if ((val & SG_MEMCONF_CH0_NUM_MASK) == SG_MEMCONF_CH0_NUM_2) |
61 | size *= 2; | |
62 | ||
04cd4e72 | 63 | dram_map[0].size = size; |
3e9952be MY |
64 | |
65 | /* set up ch1 */ | |
04cd4e72 | 66 | dram_map[1].base = dram_map[0].base + size; |
3e9952be MY |
67 | |
68 | if (val & SG_MEMCONF_SPARSEMEM) { | |
6f47c994 | 69 | if (dram_map[1].base > sparse_ch1_base) { |
3e9952be MY |
70 | pr_warn("Sparse mem is enabled, but ch0 and ch1 overlap\n"); |
71 | pr_warn("Only ch0 is available\n"); | |
04cd4e72 | 72 | dram_map[1].base = 0; |
3e9952be MY |
73 | return 0; |
74 | } | |
75 | ||
6f47c994 | 76 | dram_map[1].base = sparse_ch1_base; |
3e9952be MY |
77 | } |
78 | ||
79 | switch (val & SG_MEMCONF_CH1_SZ_MASK) { | |
80 | case SG_MEMCONF_CH1_SZ_64M: | |
81 | size = SZ_64M; | |
82 | break; | |
83 | case SG_MEMCONF_CH1_SZ_128M: | |
84 | size = SZ_128M; | |
85 | break; | |
86 | case SG_MEMCONF_CH1_SZ_256M: | |
87 | size = SZ_256M; | |
88 | break; | |
89 | case SG_MEMCONF_CH1_SZ_512M: | |
90 | size = SZ_512M; | |
91 | break; | |
92 | case SG_MEMCONF_CH1_SZ_1G: | |
93 | size = SZ_1G; | |
94 | break; | |
95 | default: | |
0f5bf09c | 96 | pr_err("error: invalid value is set to MEMCONF ch1 size\n"); |
ac2a1030 | 97 | return -EINVAL; |
3e9952be MY |
98 | } |
99 | ||
100 | if ((val & SG_MEMCONF_CH1_NUM_MASK) == SG_MEMCONF_CH1_NUM_2) | |
101 | size *= 2; | |
ac2a1030 | 102 | |
04cd4e72 | 103 | dram_map[1].size = size; |
cf88affa | 104 | |
6f47c994 | 105 | if (!have_ch2 || val & SG_MEMCONF_CH2_DISABLE) |
3e9952be | 106 | return 0; |
cf88affa | 107 | |
3e9952be | 108 | /* set up ch2 */ |
04cd4e72 | 109 | dram_map[2].base = dram_map[1].base + size; |
3e9952be MY |
110 | |
111 | switch (val & SG_MEMCONF_CH2_SZ_MASK) { | |
112 | case SG_MEMCONF_CH2_SZ_64M: | |
113 | size = SZ_64M; | |
114 | break; | |
115 | case SG_MEMCONF_CH2_SZ_128M: | |
116 | size = SZ_128M; | |
117 | break; | |
118 | case SG_MEMCONF_CH2_SZ_256M: | |
119 | size = SZ_256M; | |
120 | break; | |
121 | case SG_MEMCONF_CH2_SZ_512M: | |
122 | size = SZ_512M; | |
123 | break; | |
124 | case SG_MEMCONF_CH2_SZ_1G: | |
125 | size = SZ_1G; | |
126 | break; | |
127 | default: | |
0f5bf09c | 128 | pr_err("error: invalid value is set to MEMCONF ch2 size\n"); |
3e9952be MY |
129 | return -EINVAL; |
130 | } | |
131 | ||
132 | if ((val & SG_MEMCONF_CH2_NUM_MASK) == SG_MEMCONF_CH2_NUM_2) | |
133 | size *= 2; | |
134 | ||
04cd4e72 | 135 | dram_map[2].size = size; |
5894ca00 | 136 | |
5894ca00 MY |
137 | return 0; |
138 | } | |
cf88affa | 139 | |
6f47c994 MY |
140 | static int uniphier_ld4_dram_map_get(struct uniphier_dram_map dram_map[]) |
141 | { | |
142 | return uniphier_memconf_decode(dram_map, 0xc0000000, false); | |
143 | } | |
144 | ||
145 | static int uniphier_pro4_dram_map_get(struct uniphier_dram_map dram_map[]) | |
146 | { | |
147 | return uniphier_memconf_decode(dram_map, 0xa0000000, false); | |
148 | } | |
149 | ||
150 | static int uniphier_pxs2_dram_map_get(struct uniphier_dram_map dram_map[]) | |
151 | { | |
152 | return uniphier_memconf_decode(dram_map, 0xc0000000, true); | |
153 | } | |
154 | ||
155 | struct uniphier_dram_init_data { | |
156 | unsigned int soc_id; | |
157 | int (*dram_map_get)(struct uniphier_dram_map dram_map[]); | |
158 | }; | |
159 | ||
160 | static const struct uniphier_dram_init_data uniphier_dram_init_data[] = { | |
161 | { | |
162 | .soc_id = UNIPHIER_LD4_ID, | |
163 | .dram_map_get = uniphier_ld4_dram_map_get, | |
164 | }, | |
165 | { | |
166 | .soc_id = UNIPHIER_PRO4_ID, | |
167 | .dram_map_get = uniphier_pro4_dram_map_get, | |
168 | }, | |
169 | { | |
170 | .soc_id = UNIPHIER_SLD8_ID, | |
171 | .dram_map_get = uniphier_ld4_dram_map_get, | |
172 | }, | |
173 | { | |
174 | .soc_id = UNIPHIER_PRO5_ID, | |
175 | .dram_map_get = uniphier_ld4_dram_map_get, | |
176 | }, | |
177 | { | |
178 | .soc_id = UNIPHIER_PXS2_ID, | |
179 | .dram_map_get = uniphier_pxs2_dram_map_get, | |
180 | }, | |
181 | { | |
182 | .soc_id = UNIPHIER_LD6B_ID, | |
183 | .dram_map_get = uniphier_pxs2_dram_map_get, | |
184 | }, | |
185 | { | |
186 | .soc_id = UNIPHIER_LD11_ID, | |
187 | .dram_map_get = uniphier_ld4_dram_map_get, | |
188 | }, | |
189 | { | |
190 | .soc_id = UNIPHIER_LD20_ID, | |
191 | .dram_map_get = uniphier_pxs2_dram_map_get, | |
192 | }, | |
193 | { | |
194 | .soc_id = UNIPHIER_PXS3_ID, | |
195 | .dram_map_get = uniphier_pxs2_dram_map_get, | |
196 | }, | |
197 | }; | |
198 | UNIPHIER_DEFINE_SOCDATA_FUNC(uniphier_get_dram_init_data, | |
199 | uniphier_dram_init_data) | |
200 | ||
201 | static int uniphier_dram_map_get(struct uniphier_dram_map *dram_map) | |
202 | { | |
203 | const struct uniphier_dram_init_data *data; | |
204 | ||
205 | data = uniphier_get_dram_init_data(); | |
206 | if (!data) { | |
207 | pr_err("unsupported SoC\n"); | |
208 | return -ENOTSUPP; | |
209 | } | |
210 | ||
211 | return data->dram_map_get(dram_map); | |
212 | } | |
213 | ||
3e9952be | 214 | int dram_init(void) |
cf88affa | 215 | { |
04cd4e72 | 216 | struct uniphier_dram_map dram_map[3] = {}; |
df725341 MY |
217 | bool valid_bank_found = false; |
218 | unsigned long prev_top; | |
3e9952be MY |
219 | int ret, i; |
220 | ||
221 | gd->ram_size = 0; | |
222 | ||
6f47c994 | 223 | ret = uniphier_dram_map_get(dram_map); |
3e9952be MY |
224 | if (ret) |
225 | return ret; | |
226 | ||
04cd4e72 | 227 | for (i = 0; i < ARRAY_SIZE(dram_map); i++) { |
be893a5c | 228 | unsigned long max_size; |
3e9952be | 229 | |
04cd4e72 | 230 | if (!dram_map[i].size) |
df725341 | 231 | continue; |
3e9952be MY |
232 | |
233 | /* | |
234 | * U-Boot relocates itself to the tail of the memory region, | |
235 | * but it does not expect sparse memory. We use the first | |
236 | * contiguous chunk here. | |
237 | */ | |
df725341 | 238 | if (valid_bank_found && prev_top < dram_map[i].base) |
3e9952be MY |
239 | break; |
240 | ||
be893a5c MY |
241 | /* |
242 | * Do not use memory that exceeds 32bit address range. U-Boot | |
243 | * relocates itself to the end of the effectively available RAM. | |
244 | * This could be a problem for DMA engines that do not support | |
245 | * 64bit address (SDMA of SDHCI, UniPhier AV-ether, etc.) | |
246 | */ | |
247 | if (dram_map[i].base >= 1ULL << 32) | |
248 | break; | |
249 | ||
250 | max_size = (1ULL << 32) - dram_map[i].base; | |
251 | ||
6da69d33 | 252 | gd->ram_size = min(dram_map[i].size, max_size); |
df725341 | 253 | |
1f8357c3 MY |
254 | if (!valid_bank_found) |
255 | gd->ram_base = dram_map[i].base; | |
256 | ||
df725341 MY |
257 | prev_top = dram_map[i].base + dram_map[i].size; |
258 | valid_bank_found = true; | |
ac2a1030 MY |
259 | } |
260 | ||
a322eb9f MY |
261 | /* |
262 | * LD20 uses the last 64 byte for each channel for dynamic | |
263 | * DDR PHY training | |
264 | */ | |
265 | if (uniphier_get_soc_id() == UNIPHIER_LD20_ID) | |
266 | gd->ram_size -= 64; | |
267 | ||
4341fb73 KH |
268 | /* map all the DRAM regions */ |
269 | uniphier_mem_map_init(gd->ram_base, prev_top - gd->ram_base); | |
270 | ||
3e9952be MY |
271 | return 0; |
272 | } | |
273 | ||
76b00aca | 274 | int dram_init_banksize(void) |
3e9952be | 275 | { |
04cd4e72 | 276 | struct uniphier_dram_map dram_map[3] = {}; |
6f47c994 | 277 | int ret, i; |
ac2a1030 | 278 | |
6f47c994 MY |
279 | ret = uniphier_dram_map_get(dram_map); |
280 | if (ret) | |
281 | return ret; | |
cf88affa | 282 | |
04cd4e72 | 283 | for (i = 0; i < ARRAY_SIZE(dram_map); i++) { |
34e29f7d MY |
284 | if (i < ARRAY_SIZE(gd->bd->bi_dram)) { |
285 | gd->bd->bi_dram[i].start = dram_map[i].base; | |
286 | gd->bd->bi_dram[i].size = dram_map[i].size; | |
287 | } | |
288 | ||
289 | if (!dram_map[i].size) | |
290 | continue; | |
cf88affa | 291 | } |
76b00aca SG |
292 | |
293 | return 0; | |
cf88affa | 294 | } |